Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
namespace Aws\EndpointV2;
3
 
4
use Aws\Api\Operation;
5
use Aws\Api\Service;
6
use Aws\Auth\Exception\UnresolvedAuthSchemeException;
7
use Aws\CommandInterface;
8
use Aws\MetricsBuilder;
9
use Closure;
10
use GuzzleHttp\Promise\Promise;
11
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
12
use function JmesPath\search;
13
 
14
/**
15
 * Handles endpoint rule evaluation and endpoint resolution.
16
 *
17
 * IMPORTANT: this middleware must be added to the "build" step.
18
 * Specifically, it must precede the 'builder' step.
19
 *
20
 * @internal
21
 */
22
class EndpointV2Middleware
23
{
24
    const ACCOUNT_ID_PARAM = 'AccountId';
25
    const ACCOUNT_ID_ENDPOINT_MODE_PARAM = 'AccountIdEndpointMode';
26
    private static $validAuthSchemes = [
27
        'sigv4' => 'v4',
28
        'sigv4a' => 'v4a',
29
        'none' => 'anonymous',
30
        'bearer' => 'bearer',
31
        'sigv4-s3express' => 'v4-s3express'
32
    ];
33
 
34
    /** @var callable */
35
    private $nextHandler;
36
 
37
    /** @var EndpointProviderV2 */
38
    private $endpointProvider;
39
 
40
    /** @var Service */
41
    private $api;
42
 
43
    /** @var array */
44
    private $clientArgs;
45
 
46
    /** @var Closure */
47
    private $credentialProvider;
48
 
49
    /**
50
     * Create a middleware wrapper function
51
     *
52
     * @param EndpointProviderV2 $endpointProvider
53
     * @param Service $api
54
     * @param array $args
55
     * @param callable $credentialProvider
56
     *
57
     * @return Closure
58
     */
59
    public static function wrap(
60
        EndpointProviderV2 $endpointProvider,
61
        Service $api,
62
        array $args,
63
        callable $credentialProvider
64
    ) : Closure
65
    {
66
        return function (callable $handler) use ($endpointProvider, $api, $args, $credentialProvider) {
67
            return new self($handler, $endpointProvider, $api, $args, $credentialProvider);
68
        };
69
    }
70
 
71
    /**
72
     * @param callable $nextHandler
73
     * @param EndpointProviderV2 $endpointProvider
74
     * @param Service $api
75
     * @param array $args
76
     */
77
    public function __construct(
78
        callable $nextHandler,
79
        EndpointProviderV2 $endpointProvider,
80
        Service $api,
81
        array $args,
82
        ?callable $credentialProvider = null
83
    )
84
    {
85
        $this->nextHandler = $nextHandler;
86
        $this->endpointProvider = $endpointProvider;
87
        $this->api = $api;
88
        $this->clientArgs = $args;
89
        $this->credentialProvider = $credentialProvider;
90
    }
91
 
92
    /**
93
     * @param CommandInterface $command
94
     *
95
     * @return Promise
96
     */
97
    public function __invoke(CommandInterface $command)
98
    {
99
        $nextHandler = $this->nextHandler;
100
        $operation = $this->api->getOperation($command->getName());
101
        $commandArgs = $command->toArray();
102
        $providerArgs = $this->resolveArgs($commandArgs, $operation);
103
 
104
        $endpoint = $this->endpointProvider->resolveEndpoint($providerArgs);
105
 
106
        $this->appendEndpointMetrics($providerArgs, $endpoint, $command);
107
 
108
        if (!empty($authSchemes = $endpoint->getProperty('authSchemes'))) {
109
            $this->applyAuthScheme(
110
                $authSchemes,
111
                $command
112
            );
113
        }
114
 
115
        return $nextHandler($command, $endpoint);
116
    }
117
 
118
    /**
119
     * Resolves client, context params, static context params and endpoint provider
120
     * arguments provided at the command level.
121
     *
122
     * @param array $commandArgs
123
     * @param Operation $operation
124
     *
125
     * @return array
126
     */
127
    private function resolveArgs(array $commandArgs, Operation $operation): array
128
    {
129
        $rulesetParams = $this->endpointProvider->getRuleset()->getParameters();
130
 
131
        if (isset($rulesetParams[self::ACCOUNT_ID_PARAM])
132
            && isset($rulesetParams[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM])) {
133
            $this->clientArgs[self::ACCOUNT_ID_PARAM] = $this->resolveAccountId();
134
        }
135
 
136
        $endpointCommandArgs = $this->filterEndpointCommandArgs(
137
            $rulesetParams,
138
            $commandArgs
139
        );
140
        $staticContextParams = $this->bindStaticContextParams(
141
            $operation->getStaticContextParams()
142
        );
143
        $contextParams = $this->bindContextParams(
144
            $commandArgs, $operation->getContextParams()
145
        );
146
        $operationContextParams = $this->bindOperationContextParams(
147
            $commandArgs,
148
            $operation->getOperationContextParams()
149
        );
150
 
151
        return array_merge(
152
            $this->clientArgs,
153
            $operationContextParams,
154
            $contextParams,
155
            $staticContextParams,
156
            $endpointCommandArgs
157
        );
158
    }
159
 
160
    /**
161
     * Compares Ruleset parameters against Command arguments
162
     * to create a mapping of arguments to pass into the
163
     * endpoint provider for endpoint resolution.
164
     *
165
     * @param array $rulesetParams
166
     * @param array $commandArgs
167
     * @return array
168
     */
169
    private function filterEndpointCommandArgs(
170
        array $rulesetParams,
171
        array $commandArgs
172
    ): array
173
    {
174
        $endpointMiddlewareOpts = [
175
            '@use_dual_stack_endpoint' => 'UseDualStack',
176
            '@use_accelerate_endpoint' => 'Accelerate',
177
            '@use_path_style_endpoint' => 'ForcePathStyle'
178
        ];
179
 
180
        $filteredArgs = [];
181
 
182
        foreach($rulesetParams as $name => $value) {
183
            if (isset($commandArgs[$name])) {
184
                if (!empty($value->getBuiltIn())) {
185
                    continue;
186
                }
187
                $filteredArgs[$name] = $commandArgs[$name];
188
            }
189
        }
190
 
191
        if ($this->api->getServiceName() === 's3') {
192
            foreach($endpointMiddlewareOpts as $optionName => $newValue) {
193
                if (isset($commandArgs[$optionName])) {
194
                    $filteredArgs[$newValue] = $commandArgs[$optionName];
195
                }
196
            }
197
        }
198
 
199
        return $filteredArgs;
200
    }
201
 
202
    /**
203
     * Binds static context params to their corresponding values.
204
     *
205
     * @param $staticContextParams
206
     *
207
     * @return array
208
     */
209
    private function bindStaticContextParams($staticContextParams): array
210
    {
211
        $scopedParams = [];
212
 
213
        forEach($staticContextParams as $paramName => $paramValue) {
214
            $scopedParams[$paramName] = $paramValue['value'];
215
        }
216
 
217
        return $scopedParams;
218
    }
219
 
220
    /**
221
     * Binds context params to their corresponding values found in
222
     * command arguments.
223
     *
224
     * @param array $commandArgs
225
     * @param array $contextParams
226
     *
227
     * @return array
228
     */
229
    private function bindContextParams(
230
        array $commandArgs,
231
        array $contextParams
232
    ): array
233
    {
234
        $scopedParams = [];
235
 
236
        foreach($contextParams as $name => $spec) {
237
            if (isset($commandArgs[$spec['shape']])) {
238
                $scopedParams[$name] = $commandArgs[$spec['shape']];
239
            }
240
        }
241
 
242
        return $scopedParams;
243
    }
244
 
245
    /**
246
     * Binds context params to their corresponding values found in
247
     * command arguments.
248
     *
249
     * @param array $commandArgs
250
     * @param array $contextParams
251
     *
252
     * @return array
253
     */
254
    private function bindOperationContextParams(
255
        array $commandArgs,
256
        array $operationContextParams
257
    ): array
258
    {
259
        $scopedParams = [];
260
 
261
        foreach($operationContextParams as $name => $spec) {
262
            $scopedValue = search($spec['path'], $commandArgs);
263
 
264
            if ($scopedValue) {
265
                $scopedParams[$name] = $scopedValue;
266
            }
267
        }
268
 
269
        return $scopedParams;
270
    }
271
 
272
    /**
273
     * Applies resolved auth schemes to the command object.
274
     *
275
     * @param $authSchemes
276
     * @param $command
277
     *
278
     * @return void
279
     */
280
    private function applyAuthScheme(
281
        array $authSchemes,
282
        CommandInterface $command
283
    ): void
284
    {
285
        $authScheme = $this->resolveAuthScheme($authSchemes);
286
 
287
        $command['@context']['signature_version'] = $authScheme['version'];
288
 
289
        if (isset($authScheme['name'])) {
290
            $command['@context']['signing_service'] = $authScheme['name'];
291
        }
292
 
293
        if (isset($authScheme['region'])) {
294
            $command['@context']['signing_region'] = $authScheme['region'];
295
        } elseif (isset($authScheme['signingRegionSet'])) {
296
            $command['@context']['signing_region_set'] = $authScheme['signingRegionSet'];
297
        }
298
    }
299
 
300
    /**
301
     * Returns the first compatible auth scheme in an endpoint object's
302
     * auth schemes.
303
     *
304
     * @param array $authSchemes
305
     *
306
     * @return array
307
     */
308
    private function resolveAuthScheme(array $authSchemes): array
309
    {
310
        $invalidAuthSchemes = [];
311
 
312
        foreach($authSchemes as $authScheme) {
313
            if ($this->isValidAuthScheme($authScheme['name'])) {
314
                return $this->normalizeAuthScheme($authScheme);
315
            }
316
            $invalidAuthSchemes[$authScheme['name']] = false;
317
        }
318
 
319
        $invalidAuthSchemesString = '`' . implode(
320
            '`, `',
321
            array_keys($invalidAuthSchemes))
322
            . '`';
323
        $validAuthSchemesString = '`'
324
            . implode('`, `', array_keys(
325
                array_diff_key(self::$validAuthSchemes, $invalidAuthSchemes))
326
            )
327
            . '`';
328
        throw new UnresolvedAuthSchemeException(
329
            "This operation requests {$invalidAuthSchemesString}"
330
            . " auth schemes, but the client currently supports {$validAuthSchemesString}."
331
        );
332
    }
333
 
334
    /**
335
     * Normalizes an auth scheme's name, signing region or signing region set
336
     * to the auth keys recognized by the SDK.
337
     *
338
     * @param array $authScheme
339
     * @return array
340
     */
341
    private function normalizeAuthScheme(array $authScheme): array
342
    {
343
        /*
344
            sigv4a will contain a regionSet property. which is guaranteed to be `*`
345
            for now.  The SigV4 class handles this automatically for now. It seems
346
            complexity will be added here in the future.
347
       */
348
        $normalizedAuthScheme = [];
349
 
350
        if (isset($authScheme['disableDoubleEncoding'])
351
            && $authScheme['disableDoubleEncoding'] === true
352
            && $authScheme['name'] !== 'sigv4a'
353
            && $authScheme['name'] !== 'sigv4-s3express'
354
        ) {
355
            $normalizedAuthScheme['version'] = 's3v4';
356
        } else {
357
            $normalizedAuthScheme['version'] = self::$validAuthSchemes[$authScheme['name']];
358
        }
359
 
360
        $normalizedAuthScheme['name'] = $authScheme['signingName'] ?? null;
361
        $normalizedAuthScheme['region'] = $authScheme['signingRegion'] ?? null;
362
        $normalizedAuthScheme['signingRegionSet'] = $authScheme['signingRegionSet'] ?? null;
363
 
364
        return $normalizedAuthScheme;
365
    }
366
 
367
    private function isValidAuthScheme($signatureVersion): bool
368
    {
369
        if (isset(self::$validAuthSchemes[$signatureVersion])) {
370
              if ($signatureVersion === 'sigv4a') {
371
                  return extension_loaded('awscrt');
372
              }
373
              return true;
374
        }
375
 
376
        return false;
377
    }
378
 
379
    /**
380
     * This method tries to resolve an `AccountId` parameter from a resolved identity.
381
     * We will just perform this operation if the parameter `AccountId` is part of the ruleset parameters and
382
     * `AccountIdEndpointMode` is not disabled, otherwise, we will ignore it.
383
     *
384
     * @return null|string
385
     */
386
    private function resolveAccountId(): ?string
387
    {
388
        if (isset($this->clientArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM])
389
            && $this->clientArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM] === 'disabled') {
390
            return null;
391
        }
392
 
393
        if (is_null($this->credentialProvider)) {
394
            return null;
395
        }
396
 
397
        $identityProviderFn = $this->credentialProvider;
398
        $identity = $identityProviderFn()->wait();
399
 
400
        return $identity->getAccountId();
401
    }
402
 
403
    private function appendEndpointMetrics(
404
        array $providerArgs,
405
        RulesetEndpoint $endpoint,
406
        CommandInterface $command
407
    ): void
408
    {
409
        // Resolved AccountId Metric
410
        if (!empty($providerArgs[self::ACCOUNT_ID_PARAM])) {
411
            $command->getMetricsBuilder()->append(MetricsBuilder::RESOLVED_ACCOUNT_ID);
412
        }
413
        // AccountIdMode Metric
414
        if(!empty($providerArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM])) {
415
            $command->getMetricsBuilder()->identifyMetricByValueAndAppend(
416
                'account_id_endpoint_mode',
417
                $providerArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM]
418
            );
419
        }
420
 
421
        // AccountId Endpoint Metric
422
        $command->getMetricsBuilder()->identifyMetricByValueAndAppend(
423
            'account_id_endpoint',
424
            $endpoint->getUrl()
425
        );
426
    }
427
}