PluginProbe
Media Cloud Sync / 1.2.13
Media Cloud Sync v1.2.13
1.4.2 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 All 36 releases
media-cloud-sync / includes / sdk / s3 / Aws / EndpointV2 / EndpointV2Middleware.php

EndpointV2Middleware.php in Media Cloud Sync 1.2.13, at includes/sdk/s3/Aws/EndpointV2/EndpointV2Middleware.php

262 lines 10.0 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\EndpointV2;
4
5 use Dudlewebs\WPMCS\s3\Aws\Api\Operation;
6 use Dudlewebs\WPMCS\s3\Aws\Api\Service;
7 use Dudlewebs\WPMCS\s3\Aws\Auth\Exception\UnresolvedAuthSchemeException;
8 use Dudlewebs\WPMCS\s3\Aws\CommandInterface;
9 use Closure;
10 use Dudlewebs\WPMCS\s3\GuzzleHttp\Promise\Promise;
11 /**
12 * Handles endpoint rule evaluation and endpoint resolution.
13 *
14 * IMPORTANT: this middleware must be added to the "build" step.
15 * Specifically, it must precede the 'builder' step.
16 *
17 * @internal
18 */
19 class EndpointV2Middleware
20 {
21 const ACCOUNT_ID_PARAM = 'AccountId';
22 const ACCOUNT_ID_ENDPOINT_MODE_PARAM = 'AccountIdEndpointMode';
23 private static $validAuthSchemes = ['sigv4' => 'v4', 'sigv4a' => 'v4a', 'none' => 'anonymous', 'bearer' => 'bearer', 'sigv4-s3express' => 'v4-s3express'];
24 /** @var callable */
25 private $nextHandler;
26 /** @var EndpointProviderV2 */
27 private $endpointProvider;
28 /** @var Service */
29 private $api;
30 /** @var array */
31 private $clientArgs;
32 /** @var Closure */
33 private $credentialProvider;
34 /**
35 * Create a middleware wrapper function
36 *
37 * @param EndpointProviderV2 $endpointProvider
38 * @param Service $api
39 * @param array $args
40 * @param callable $credentialProvider
41 *
42 * @return Closure
43 */
44 public static function wrap(EndpointProviderV2 $endpointProvider, Service $api, array $args, callable $credentialProvider) : Closure
45 {
46 return function (callable $handler) use($endpointProvider, $api, $args, $credentialProvider) {
47 return new self($handler, $endpointProvider, $api, $args, $credentialProvider);
48 };
49 }
50 /**
51 * @param callable $nextHandler
52 * @param EndpointProviderV2 $endpointProvider
53 * @param Service $api
54 * @param array $args
55 */
56 public function __construct(callable $nextHandler, EndpointProviderV2 $endpointProvider, Service $api, array $args, callable $credentialProvider = null)
57 {
58 $this->nextHandler = $nextHandler;
59 $this->endpointProvider = $endpointProvider;
60 $this->api = $api;
61 $this->clientArgs = $args;
62 $this->credentialProvider = $credentialProvider;
63 }
64 /**
65 * @param CommandInterface $command
66 *
67 * @return Promise
68 */
69 public function __invoke(CommandInterface $command)
70 {
71 $nextHandler = $this->nextHandler;
72 $operation = $this->api->getOperation($command->getName());
73 $commandArgs = $command->toArray();
74 $providerArgs = $this->resolveArgs($commandArgs, $operation);
75 $endpoint = $this->endpointProvider->resolveEndpoint($providerArgs);
76 if (!empty($authSchemes = $endpoint->getProperty('authSchemes'))) {
77 $this->applyAuthScheme($authSchemes, $command);
78 }
79 return $nextHandler($command, $endpoint);
80 }
81 /**
82 * Resolves client, context params, static context params and endpoint provider
83 * arguments provided at the command level.
84 *
85 * @param array $commandArgs
86 * @param Operation $operation
87 *
88 * @return array
89 */
90 private function resolveArgs(array $commandArgs, Operation $operation) : array
91 {
92 $rulesetParams = $this->endpointProvider->getRuleset()->getParameters();
93 if (isset($rulesetParams[self::ACCOUNT_ID_PARAM]) && isset($rulesetParams[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM])) {
94 $this->clientArgs[self::ACCOUNT_ID_PARAM] = $this->resolveAccountId();
95 }
96 $endpointCommandArgs = $this->filterEndpointCommandArgs($rulesetParams, $commandArgs);
97 $staticContextParams = $this->bindStaticContextParams($operation->getStaticContextParams());
98 $contextParams = $this->bindContextParams($commandArgs, $operation->getContextParams());
99 return \array_merge($this->clientArgs, $contextParams, $staticContextParams, $endpointCommandArgs);
100 }
101 /**
102 * Compares Ruleset parameters against Command arguments
103 * to create a mapping of arguments to pass into the
104 * endpoint provider for endpoint resolution.
105 *
106 * @param array $rulesetParams
107 * @param array $commandArgs
108 * @return array
109 */
110 private function filterEndpointCommandArgs(array $rulesetParams, array $commandArgs) : array
111 {
112 $endpointMiddlewareOpts = ['@use_dual_stack_endpoint' => 'UseDualStack', '@use_accelerate_endpoint' => 'Accelerate', '@use_path_style_endpoint' => 'ForcePathStyle'];
113 $filteredArgs = [];
114 foreach ($rulesetParams as $name => $value) {
115 if (isset($commandArgs[$name])) {
116 if (!empty($value->getBuiltIn())) {
117 continue;
118 }
119 $filteredArgs[$name] = $commandArgs[$name];
120 }
121 }
122 if ($this->api->getServiceName() === 's3') {
123 foreach ($endpointMiddlewareOpts as $optionName => $newValue) {
124 if (isset($commandArgs[$optionName])) {
125 $filteredArgs[$newValue] = $commandArgs[$optionName];
126 }
127 }
128 }
129 return $filteredArgs;
130 }
131 /**
132 * Binds static context params to their corresponding values.
133 *
134 * @param $staticContextParams
135 *
136 * @return array
137 */
138 private function bindStaticContextParams($staticContextParams) : array
139 {
140 $scopedParams = [];
141 foreach ($staticContextParams as $paramName => $paramValue) {
142 $scopedParams[$paramName] = $paramValue['value'];
143 }
144 return $scopedParams;
145 }
146 /**
147 * Binds context params to their corresponding values found in
148 * command arguments.
149 *
150 * @param array $commandArgs
151 * @param array $contextParams
152 *
153 * @return array
154 */
155 private function bindContextParams(array $commandArgs, array $contextParams) : array
156 {
157 $scopedParams = [];
158 foreach ($contextParams as $name => $spec) {
159 if (isset($commandArgs[$spec['shape']])) {
160 $scopedParams[$name] = $commandArgs[$spec['shape']];
161 }
162 }
163 return $scopedParams;
164 }
165 /**
166 * Applies resolved auth schemes to the command object.
167 *
168 * @param $authSchemes
169 * @param $command
170 *
171 * @return void
172 */
173 private function applyAuthScheme(array $authSchemes, CommandInterface $command) : void
174 {
175 $authScheme = $this->resolveAuthScheme($authSchemes);
176 $command['@context']['signature_version'] = $authScheme['version'];
177 if (isset($authScheme['name'])) {
178 $command['@context']['signing_service'] = $authScheme['name'];
179 }
180 if (isset($authScheme['region'])) {
181 $command['@context']['signing_region'] = $authScheme['region'];
182 } elseif (isset($authScheme['signingRegionSet'])) {
183 $command['@context']['signing_region_set'] = $authScheme['signingRegionSet'];
184 }
185 }
186 /**
187 * Returns the first compatible auth scheme in an endpoint object's
188 * auth schemes.
189 *
190 * @param array $authSchemes
191 *
192 * @return array
193 */
194 private function resolveAuthScheme(array $authSchemes) : array
195 {
196 $invalidAuthSchemes = [];
197 foreach ($authSchemes as $authScheme) {
198 if ($this->isValidAuthScheme($authScheme['name'])) {
199 return $this->normalizeAuthScheme($authScheme);
200 }
201 $invalidAuthSchemes[$authScheme['name']] = \false;
202 }
203 $invalidAuthSchemesString = '`' . \implode('`, `', \array_keys($invalidAuthSchemes)) . '`';
204 $validAuthSchemesString = '`' . \implode('`, `', \array_keys(\array_diff_key(self::$validAuthSchemes, $invalidAuthSchemes))) . '`';
205 throw new UnresolvedAuthSchemeException("This operation requests {$invalidAuthSchemesString}" . " auth schemes, but the client currently supports {$validAuthSchemesString}.");
206 }
207 /**
208 * Normalizes an auth scheme's name, signing region or signing region set
209 * to the auth keys recognized by the SDK.
210 *
211 * @param array $authScheme
212 * @return array
213 */
214 private function normalizeAuthScheme(array $authScheme) : array
215 {
216 /*
217 sigv4a will contain a regionSet property. which is guaranteed to be `*`
218 for now. The SigV4 class handles this automatically for now. It seems
219 complexity will be added here in the future.
220 */
221 $normalizedAuthScheme = [];
222 if (isset($authScheme['disableDoubleEncoding']) && $authScheme['disableDoubleEncoding'] === \true && $authScheme['name'] !== 'sigv4a' && $authScheme['name'] !== 'sigv4-s3express') {
223 $normalizedAuthScheme['version'] = 's3v4';
224 } else {
225 $normalizedAuthScheme['version'] = self::$validAuthSchemes[$authScheme['name']];
226 }
227 $normalizedAuthScheme['name'] = $authScheme['signingName'] ?? null;
228 $normalizedAuthScheme['region'] = $authScheme['signingRegion'] ?? null;
229 $normalizedAuthScheme['signingRegionSet'] = $authScheme['signingRegionSet'] ?? null;
230 return $normalizedAuthScheme;
231 }
232 private function isValidAuthScheme($signatureVersion) : bool
233 {
234 if (isset(self::$validAuthSchemes[$signatureVersion])) {
235 if ($signatureVersion === 'sigv4a') {
236 return \extension_loaded('awscrt');
237 }
238 return \true;
239 }
240 return \false;
241 }
242 /**
243 * This method tries to resolve an `AccountId` parameter from a resolved identity.
244 * We will just perform this operation if the parameter `AccountId` is part of the ruleset parameters and
245 * `AccountIdEndpointMode` is not disabled, otherwise, we will ignore it.
246 *
247 * @return null|string
248 */
249 private function resolveAccountId() : ?string
250 {
251 if (isset($this->clientArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM]) && $this->clientArgs[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM] === 'disabled') {
252 return null;
253 }
254 if (\is_null($this->credentialProvider)) {
255 return null;
256 }
257 $identityProviderFn = $this->credentialProvider;
258 $identity = $identityProviderFn()->wait();
259 return $identity->getAccountId();
260 }
261 }
262