Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\S3Control;
3
 
4
use Aws\Api\Service;
5
use Aws\Arn\AccessPointArnInterface;
6
use Aws\Arn\ArnInterface;
7
use Aws\Arn\ArnParser;
8
use Aws\Arn\Exception\InvalidArnException;
9
use Aws\Arn\S3\BucketArnInterface;
10
use Aws\Arn\S3\OutpostsArnInterface;
11
use Aws\CommandInterface;
12
use Aws\Endpoint\PartitionEndpointProvider;
13
use Aws\Exception\InvalidRegionException;
14
use Aws\Exception\UnresolvedEndpointException;
15
use Aws\S3\EndpointRegionHelperTrait;
16
use GuzzleHttp\Psr7;
17
use Psr\Http\Message\RequestInterface;
18
 
19
/**
20
 * Checks for access point ARN in members targeting BucketName, modifying
21
 * endpoint as appropriate
22
 *
23
 * @internal
24
 */
25
class EndpointArnMiddleware
26
{
27
    use EndpointRegionHelperTrait;
28
 
29
    /**
30
     * Commands which do not do ARN expansion for a specific given shape name
31
     * @var array
32
     */
33
    private static $selectiveNonArnableCmds = [
34
        'AccessPointName' => [
35
            'CreateAccessPoint',
36
        ],
37
        'BucketName' => [],
38
    ];
39
 
40
    /**
41
     * Commands which do not do ARN expansion at all for relevant members
42
     * @var array
43
     */
44
    private static $nonArnableCmds = [
45
        'CreateBucket',
46
        'ListRegionalBuckets',
47
    ];
48
 
49
    /**
50
     * Commands which trigger endpoint and signer redirection based on presence
51
     * of OutpostId
52
     * @var array
53
     */
54
    private static $outpostIdRedirectCmds = [
55
        'CreateBucket',
56
        'ListRegionalBuckets',
57
    ];
58
 
59
    /** @var callable */
60
    private $nextHandler;
61
 
62
    /** @var boolean */
63
    private $isUseEndpointV2;
64
 
65
    /**
66
     * Create a middleware wrapper function.
67
     *
68
     * @param Service $service
69
     * @param $region
70
     * @param array $config
71
     * @return callable
72
     */
73
    public static function wrap(
74
        Service $service,
75
                $region,
76
        array   $config,
77
                $isUseEndpointV2
78
    )
79
    {
80
        return function (callable $handler) use ($service, $region, $config, $isUseEndpointV2) {
81
            return new self($handler, $service, $region, $config, $isUseEndpointV2);
82
        };
83
    }
84
 
85
    public function __construct(
86
        callable $nextHandler,
87
        Service  $service,
88
                 $region,
89
        array    $config = [],
90
        $isUseEndpointV2 = false
91
    )
92
    {
93
        $this->partitionProvider = PartitionEndpointProvider::defaultProvider();
94
        $this->region = $region;
95
        $this->service = $service;
96
        $this->config = $config;
97
        $this->nextHandler = $nextHandler;
98
        $this->isUseEndpointV2 = $isUseEndpointV2;
99
    }
100
 
101
    public function __invoke(CommandInterface $cmd, RequestInterface $req)
102
    {
103
        $nextHandler = $this->nextHandler;
104
 
105
        $op = $this->service->getOperation($cmd->getName())->toArray();
106
        if (!empty($op['input']['shape'])
107
            && !in_array($cmd->getName(), self::$nonArnableCmds)
108
        ) {
109
            $service = $this->service->toArray();
110
            if (!empty($input = $service['shapes'][$op['input']['shape']])) {
111
 
112
                // Stores member name that targets 'BucketName' shape
113
                $bucketNameMember = null;
114
 
115
                // Stores member name that targets 'AccessPointName' shape
116
                $accesspointNameMember = null;
117
 
118
                foreach ($input['members'] as $key => $member) {
119
                    if ($member['shape'] === 'BucketName') {
120
                        $bucketNameMember = $key;
121
                    }
122
                    if ($member['shape'] === 'AccessPointName') {
123
                        $accesspointNameMember = $key;
124
                    }
125
                }
126
 
127
                // Determine if appropriate member contains ARN value and is
128
                // eligible for ARN expansion
129
                if (!is_null($bucketNameMember)
130
                    && !empty($cmd[$bucketNameMember])
131
                    && !in_array($cmd->getName(), self::$selectiveNonArnableCmds['BucketName'])
132
                    && ArnParser::isArn($cmd[$bucketNameMember])
133
                ) {
134
                    $arn = ArnParser::parse($cmd[$bucketNameMember]);
135
                    !$this->isUseEndpointV2 && $partition = $this->validateBucketArn($arn);
136
                } elseif (!is_null($accesspointNameMember)
137
                    && !empty($cmd[$accesspointNameMember])
138
                    && !in_array($cmd->getName(), self::$selectiveNonArnableCmds['AccessPointName'])
139
                    && ArnParser::isArn($cmd[$accesspointNameMember])
140
                ) {
141
                    $arn = ArnParser::parse($cmd[$accesspointNameMember]);
142
                    !$this->isUseEndpointV2 && $partition = $this->validateAccessPointArn($arn);
143
                }
144
 
145
                // Process only if an appropriate member contains an ARN value
146
                // and is an Outposts ARN
147
                if (!empty($arn) && $arn instanceof OutpostsArnInterface) {
148
                    if (!$this->isUseEndpointV2) {
149
                        // Generate host based on ARN
150
                        $host = $this->generateOutpostsArnHost($arn, $req);
151
                        $req = $req->withHeader('x-amz-outpost-id', $arn->getOutpostId());
152
                    }
153
 
154
                    // ARN replacement
155
                    $path = $req->getUri()->getPath();
156
                    if ($arn instanceof AccessPointArnInterface) {
157
                        // Replace ARN with access point name
158
                        $path = str_replace(
159
                            urlencode($cmd[$accesspointNameMember]),
160
                            $arn->getAccesspointName(),
161
                            $path
162
                        );
163
 
164
                        // Replace ARN in the payload
165
                        $req->getBody()->seek(0);
166
                        $body = Psr7\Utils::streamFor(str_replace(
167
                            $cmd[$accesspointNameMember],
168
                            $arn->getAccesspointName(),
169
                            $req->getBody()->getContents()
170
                        ));
171
 
172
                        // Replace ARN in the command
173
                        $cmd[$accesspointNameMember] = $arn->getAccesspointName();
174
                    } elseif ($arn instanceof BucketArnInterface) {
175
 
176
                        // Replace ARN in the path
177
                        $path = str_replace(
178
                            urlencode($cmd[$bucketNameMember]),
179
                            $arn->getBucketName(),
180
                            $path
181
                        );
182
 
183
                        // Replace ARN in the payload
184
                        $req->getBody()->seek(0);
185
                        $newBody = str_replace(
186
                            $cmd[$bucketNameMember],
187
                            $arn->getBucketName(),
188
                            $req->getBody()->getContents()
189
                        );
190
                        $body = Psr7\Utils::streamFor($newBody);
191
 
192
                        // Replace ARN in the command
193
                        $cmd[$bucketNameMember] = $arn->getBucketName();
194
                    }
195
 
196
                    // Validate or set account ID in command
197
                    if (isset($cmd['AccountId'])) {
198
                        if ($cmd['AccountId'] !== $arn->getAccountId()) {
199
                            throw new \InvalidArgumentException("The account ID"
200
                                . " supplied in the command ({$cmd['AccountId']})"
201
                                . " does not match the account ID supplied in the"
202
                                . " ARN (" . $arn->getAccountId() . ").");
203
                        }
204
                    } else {
205
                        $cmd['AccountId'] = $arn->getAccountId();
206
                    }
207
 
208
                    // Set modified request
209
                    if (isset($body)) {
210
                        $req = $req->withBody($body);
211
                    }
212
                    if ($this->isUseEndpointV2) {
213
                        $req = $req->withUri($req->getUri()->withPath($path));
214
                        goto next;
215
                    }
216
                    $req = $req
217
                        ->withUri($req->getUri()->withHost($host)->withPath($path))
218
                        ->withHeader('x-amz-account-id', $arn->getAccountId());
219
 
220
                    // Update signing region based on ARN data if configured to do so
221
                    if ($this->config['use_arn_region']->isUseArnRegion()) {
222
                        $region = $arn->getRegion();
223
                    } else {
224
                        $region = $this->region;
225
                    }
226
                    $endpointData = $partition([
227
                        'region' => $region,
228
                        'service' => $arn->getService()
229
                    ]);
230
                    $cmd['@context']['signing_region'] = $endpointData['signingRegion'];
231
 
232
                    // Update signing service for Outposts ARNs
233
                    if ($arn instanceof OutpostsArnInterface) {
234
                        $cmd['@context']['signing_service'] = $arn->getService();
235
                    }
236
 
237
 
238
                }
239
            }
240
        }
241
        if ($this->isUseEndpointV2) {
242
            goto next;
243
        }
244
        // For operations that redirect endpoint & signing service based on
245
        // presence of OutpostId member. These operations will likely not
246
        // overlap with operations that perform ARN expansion.
247
        if (in_array($cmd->getName(), self::$outpostIdRedirectCmds)
248
            && !empty($cmd['OutpostId'])
249
        ) {
250
            $req = $req->withUri(
251
                $req->getUri()->withHost($this->generateOutpostIdHost())
252
            );
253
            $cmd['@context']['signing_service'] = 's3-outposts';
254
        }
255
 
256
        next:
257
            return $nextHandler($cmd, $req);
258
    }
259
 
260
    private function generateOutpostsArnHost(
261
        OutpostsArnInterface $arn,
262
        RequestInterface $req
263
    ) {
264
        if (!empty($this->config['use_arn_region']->isUseArnRegion())) {
265
            $region = $arn->getRegion();
266
        } else {
267
            $region = $this->region;
268
        }
269
        $fipsString = $this->config['use_fips_endpoint']->isUseFipsEndpoint()
270
            ? "-fips"
271
            : "";
272
        $suffix = $this->getPartitionSuffix($arn, $this->partitionProvider);
273
        return "s3-outposts{$fipsString}.{$region}.{$suffix}";
274
    }
275
 
276
    private function generateOutpostIdHost()
277
    {
278
        $partition = $this->partitionProvider->getPartition(
279
            $this->region,
280
            $this->service->getEndpointPrefix()
281
        );
282
        $suffix = $partition->getDnsSuffix();
283
        return "s3-outposts.{$this->region}.{$suffix}";
284
    }
285
 
286
    private function validateBucketArn(ArnInterface $arn)
287
    {
288
        if ($arn instanceof BucketArnInterface) {
289
            return $this->validateArn($arn);
290
        }
291
 
292
        throw new InvalidArnException('Provided ARN was not a valid S3 bucket'
293
            . ' ARN.');
294
    }
295
 
296
    private function validateAccessPointArn(ArnInterface $arn)
297
    {
298
        if ($arn instanceof AccessPointArnInterface) {
299
            return $this->validateArn($arn);
300
        }
301
 
302
        throw new InvalidArnException('Provided ARN was not a valid S3 access'
303
            . ' point ARN.');
304
    }
305
 
306
    /**
307
     * Validates an ARN, returning a partition object corresponding to the ARN
308
     * if successful
309
     *
310
     * @param $arn
311
     * @return \Aws\Endpoint\Partition
312
     */
313
    private function validateArn(ArnInterface $arn)
314
    {
315
        // Dualstack is not supported with Outposts ARNs
316
        if ($arn instanceof OutpostsArnInterface
317
            && !empty($this->config['dual_stack'])
318
        ) {
319
            throw new UnresolvedEndpointException(
320
                'Dualstack is currently not supported with S3 Outposts ARNs.'
321
                . ' Please disable dualstack or do not supply an Outposts ARN.');
322
        }
323
 
324
        // Get partitions for ARN and client region
325
        $arnPart = $this->partitionProvider->getPartitionByName(
326
            $arn->getPartition()
327
        );
328
        $clientPart = $this->partitionProvider->getPartition(
329
            $this->region,
330
            's3'
331
        );
332
 
333
        // If client partition not found, try removing pseudo-region qualifiers
334
        if (!($clientPart->isRegionMatch($this->region, 's3'))) {
335
            $clientPart = $this->partitionProvider->getPartition(
336
                \Aws\strip_fips_pseudo_regions($this->region),
337
                's3'
338
            );
339
        }
340
 
341
        // Verify that the partition matches for supplied partition and region
342
        if ($arn->getPartition() !== $clientPart->getName()) {
343
            throw new InvalidRegionException('The supplied ARN partition'
344
                . " does not match the client's partition.");
345
        }
346
        if ($clientPart->getName() !== $arnPart->getName()) {
347
            throw new InvalidRegionException('The corresponding partition'
348
                . ' for the supplied ARN region does not match the'
349
                . " client's partition.");
350
        }
351
 
352
        // Ensure ARN region matches client region unless
353
        // configured for using ARN region over client region
354
        $this->validateMatchingRegion($arn);
355
 
356
        // Ensure it is not resolved to fips pseudo-region for S3 Outposts
357
        $this->validateFipsConfigurations($arn);
358
 
359
        return $arnPart;
360
    }
361
}