PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
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 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / s3 / Aws / TraceMiddleware.php

TraceMiddleware.php in Media Cloud Sync 1.3.11, at includes/sdk/s3/Aws/TraceMiddleware.php

244 lines 11.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;
4
5 use Dudlewebs\WPMCS\s3\Aws\Api\Service;
6 use Dudlewebs\WPMCS\s3\Aws\Exception\AwsException;
7 use Dudlewebs\WPMCS\s3\GuzzleHttp\Promise\RejectedPromise;
8 use Dudlewebs\WPMCS\s3\Psr\Http\Message\RequestInterface;
9 use Dudlewebs\WPMCS\s3\Psr\Http\Message\ResponseInterface;
10 use Dudlewebs\WPMCS\s3\Psr\Http\Message\StreamInterface;
11 use RecursiveArrayIterator;
12 use RecursiveIteratorIterator;
13 /**
14 * Traces state changes between middlewares.
15 */
16 class TraceMiddleware
17 {
18 private $prevOutput;
19 private $prevInput;
20 private $config;
21 /** @var Service */
22 private $service;
23 private static $authHeaders = ['X-Amz-Security-Token' => '[TOKEN]'];
24 private static $authStrings = [
25 // S3Signature
26 '/AWSAccessKeyId=[A-Z0-9]{20}&/i' => 'AWSAccessKeyId=[KEY]&',
27 // SignatureV4 Signature and S3Signature
28 '/Signature=.+/i' => 'Signature=[SIGNATURE]',
29 // SignatureV4 access key ID
30 '/Credential=[A-Z0-9]{20}\\//i' => 'Credential=[KEY]/',
31 // S3 signatures
32 '/AWS [A-Z0-9]{20}:.+/' => 'AWS AKI[KEY]:[SIGNATURE]',
33 // STS Presigned URLs
34 '/X-Amz-Security-Token=[^&]+/i' => 'X-Amz-Security-Token=[TOKEN]',
35 // Crypto *Stream Keys
36 '/\\["key.{27,36}Stream.{9}\\]=>\\s+.{7}\\d{2}\\) "\\X{16,64}"/U' => '["key":[CONTENT KEY]]',
37 ];
38 /**
39 * Configuration array can contain the following key value pairs.
40 *
41 * - logfn: (callable) Function that is invoked with log messages. By
42 * default, PHP's "echo" function will be utilized.
43 * - stream_size: (int) When the size of a stream is greater than this
44 * number, the stream data will not be logged. Set to "0" to not log any
45 * stream data.
46 * - scrub_auth: (bool) Set to false to disable the scrubbing of auth data
47 * from the logged messages.
48 * - http: (bool) Set to false to disable the "debug" feature of lower
49 * level HTTP adapters (e.g., verbose curl output).
50 * - auth_strings: (array) A mapping of authentication string regular
51 * expressions to scrubbed strings. These mappings are passed directly to
52 * preg_replace (e.g., preg_replace($key, $value, $debugOutput) if
53 * "scrub_auth" is set to true.
54 * - auth_headers: (array) A mapping of header names known to contain
55 * sensitive data to what the scrubbed value should be. The value of any
56 * headers contained in this array will be replaced with the if
57 * "scrub_auth" is set to true.
58 */
59 public function __construct(array $config = [], ?Service $service = null)
60 {
61 $this->config = $config + ['logfn' => function ($value) {
62 echo $value;
63 }, 'stream_size' => 524288, 'scrub_auth' => \true, 'http' => \true, 'auth_strings' => [], 'auth_headers' => []];
64 $this->config['auth_strings'] += self::$authStrings;
65 $this->config['auth_headers'] += self::$authHeaders;
66 $this->service = $service;
67 }
68 public function __invoke($step, $name)
69 {
70 $this->prevOutput = $this->prevInput = [];
71 return function (callable $next) use($step, $name) {
72 return function (CommandInterface $command, $request = null) use($next, $step, $name) {
73 $this->createHttpDebug($command);
74 $start = \microtime(\true);
75 $this->stepInput(['step' => $step, 'name' => $name, 'request' => $this->requestArray($request), 'command' => $this->commandArray($command)]);
76 return $next($command, $request)->then(function ($value) use($step, $name, $command, $start) {
77 $this->flushHttpDebug($command);
78 $this->stepOutput($start, ['step' => $step, 'name' => $name, 'result' => $this->resultArray($value), 'error' => null]);
79 return $value;
80 }, function ($reason) use($step, $name, $start, $command) {
81 $this->flushHttpDebug($command);
82 $this->stepOutput($start, ['step' => $step, 'name' => $name, 'result' => null, 'error' => $this->exceptionArray($reason)]);
83 return new RejectedPromise($reason);
84 });
85 };
86 };
87 }
88 private function stepInput($entry)
89 {
90 static $keys = ['command', 'request'];
91 $this->compareStep($this->prevInput, $entry, '-> Entering', $keys);
92 $this->write("\n");
93 $this->prevInput = $entry;
94 }
95 private function stepOutput($start, $entry)
96 {
97 static $keys = ['result', 'error'];
98 $this->compareStep($this->prevOutput, $entry, '<- Leaving', $keys);
99 $totalTime = \microtime(\true) - $start;
100 $this->write(" Inclusive step time: " . $totalTime . "\n\n");
101 $this->prevOutput = $entry;
102 }
103 private function compareStep(array $a, array $b, $title, array $keys)
104 {
105 $changes = [];
106 foreach ($keys as $key) {
107 $av = isset($a[$key]) ? $a[$key] : null;
108 $bv = isset($b[$key]) ? $b[$key] : null;
109 $this->compareArray($av, $bv, $key, $changes);
110 }
111 $str = "\n{$title} step {$b['step']}, name '{$b['name']}'";
112 $str .= "\n" . \str_repeat('-', \strlen($str) - 1) . "\n\n ";
113 $str .= $changes ? \implode("\n ", \str_replace("\n", "\n ", $changes)) : 'no changes';
114 $this->write($str . "\n");
115 }
116 private function commandArray(CommandInterface $cmd)
117 {
118 return ['instance' => \spl_object_hash($cmd), 'name' => $cmd->getName(), 'params' => $this->getRedactedArray($cmd)];
119 }
120 private function requestArray($request = null)
121 {
122 return !$request instanceof RequestInterface ? [] : \array_filter(['instance' => \spl_object_hash($request), 'method' => $request->getMethod(), 'headers' => $this->redactHeaders($request->getHeaders()), 'body' => $this->streamStr($request->getBody()), 'scheme' => $request->getUri()->getScheme(), 'port' => $request->getUri()->getPort(), 'path' => $request->getUri()->getPath(), 'query' => $request->getUri()->getQuery()]);
123 }
124 private function responseArray(?ResponseInterface $response = null)
125 {
126 return !$response ? [] : ['instance' => \spl_object_hash($response), 'statusCode' => $response->getStatusCode(), 'headers' => $this->redactHeaders($response->getHeaders()), 'body' => $this->streamStr($response->getBody())];
127 }
128 private function resultArray($value)
129 {
130 return $value instanceof ResultInterface ? ['instance' => \spl_object_hash($value), 'data' => $value->toArray()] : $value;
131 }
132 private function exceptionArray($e)
133 {
134 if (!$e instanceof \Exception) {
135 return $e;
136 }
137 $result = ['instance' => \spl_object_hash($e), 'class' => \get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString()];
138 if ($e instanceof AwsException) {
139 $result += ['type' => $e->getAwsErrorType(), 'code' => $e->getAwsErrorCode(), 'requestId' => $e->getAwsRequestId(), 'statusCode' => $e->getStatusCode(), 'result' => $this->resultArray($e->getResult()), 'request' => $this->requestArray($e->getRequest()), 'response' => $this->responseArray($e->getResponse())];
140 }
141 return $result;
142 }
143 private function compareArray($a, $b, $path, array &$diff)
144 {
145 if ($a === $b) {
146 return;
147 }
148 if (\is_array($a)) {
149 $b = (array) $b;
150 $keys = \array_unique(\array_merge(\array_keys($a), \array_keys($b)));
151 foreach ($keys as $k) {
152 if (!\array_key_exists($k, $a)) {
153 $this->compareArray(null, $b[$k], "{$path}.{$k}", $diff);
154 } elseif (!\array_key_exists($k, $b)) {
155 $this->compareArray($a[$k], null, "{$path}.{$k}", $diff);
156 } else {
157 $this->compareArray($a[$k], $b[$k], "{$path}.{$k}", $diff);
158 }
159 }
160 } elseif ($a !== null && $b === null) {
161 $diff[] = "{$path} was unset";
162 } elseif ($a === null && $b !== null) {
163 $diff[] = \sprintf("%s was set to %s", $path, $this->str($b));
164 } else {
165 $diff[] = \sprintf("%s changed from %s to %s", $path, $this->str($a), $this->str($b));
166 }
167 }
168 private function str($value)
169 {
170 if (\is_scalar($value)) {
171 return (string) $value;
172 }
173 if ($value instanceof \Exception) {
174 $value = $this->exceptionArray($value);
175 }
176 \ob_start();
177 \var_dump($value);
178 return \ob_get_clean();
179 }
180 private function streamStr(StreamInterface $body)
181 {
182 return $body->getSize() < $this->config['stream_size'] ? (string) $body : 'stream(size=' . $body->getSize() . ')';
183 }
184 private function createHttpDebug(CommandInterface $command)
185 {
186 if ($this->config['http'] && !isset($command['@http']['debug'])) {
187 $command['@http']['debug'] = \fopen('php://temp', 'w+');
188 }
189 }
190 private function flushHttpDebug(CommandInterface $command)
191 {
192 if ($res = $command['@http']['debug']) {
193 if (\is_resource($res)) {
194 \rewind($res);
195 $this->write(\stream_get_contents($res));
196 \fclose($res);
197 }
198 $command['@http']['debug'] = null;
199 }
200 }
201 private function write($value)
202 {
203 if ($this->config['scrub_auth']) {
204 foreach ($this->config['auth_strings'] as $pattern => $replacement) {
205 $value = \preg_replace_callback($pattern, function ($matches) use($replacement) {
206 return $replacement;
207 }, $value);
208 }
209 }
210 \call_user_func($this->config['logfn'], $value);
211 }
212 private function redactHeaders(array $headers)
213 {
214 if ($this->config['scrub_auth']) {
215 $headers = $this->config['auth_headers'] + $headers;
216 }
217 return $headers;
218 }
219 /**
220 * @param CommandInterface $cmd
221 * @return array
222 */
223 private function getRedactedArray(CommandInterface $cmd)
224 {
225 if (!isset($this->service["shapes"])) {
226 return $cmd->toArray();
227 }
228 $shapes = $this->service["shapes"];
229 $cmdArray = $cmd->toArray();
230 $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($cmdArray), RecursiveIteratorIterator::SELF_FIRST);
231 foreach ($iterator as $parameter => $value) {
232 if (isset($shapes[$parameter]['sensitive']) && $shapes[$parameter]['sensitive'] === \true) {
233 $redactedValue = \is_string($value) ? "[{$parameter}]" : ["[{$parameter}]"];
234 $currentDepth = $iterator->getDepth();
235 for ($subDepth = $currentDepth; $subDepth >= 0; $subDepth--) {
236 $subIterator = $iterator->getSubIterator($subDepth);
237 $subIterator->offsetSet($subIterator->key(), $subDepth === $currentDepth ? $redactedValue : $iterator->getSubIterator($subDepth + 1)->getArrayCopy());
238 }
239 }
240 }
241 return $iterator->getArrayCopy();
242 }
243 }
244