PluginProbe
Media Cloud Sync / 1.4.2
Media Cloud Sync v1.4.2
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 / S3 / Parser / ValidateResponseChecksumResultMutator.php

ValidateResponseChecksumResultMutator.php in Media Cloud Sync 1.4.2, at includes/sdk/s3/Aws/S3/Parser/ValidateResponseChecksumResultMutator.php

140 lines 5.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\Parser;
4
5 use Dudlewebs\WPMCS\s3\Aws\Api\Service;
6 use Dudlewebs\WPMCS\s3\Aws\CommandInterface;
7 use Dudlewebs\WPMCS\s3\Aws\ResultInterface;
8 use Dudlewebs\WPMCS\s3\Aws\S3\CalculatesChecksumTrait;
9 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
10 use Dudlewebs\WPMCS\s3\Psr\Http\Message\ResponseInterface;
11 /**
12 * A custom s3 result mutator that validates the response checksums.
13 *
14 * @internal
15 */
16 final class ValidateResponseChecksumResultMutator implements S3ResultMutator
17 {
18 use CalculatesChecksumTrait;
19 public const DEFAULT_VALIDATION_MODE = 'when_supported';
20 /** @var Service $api */
21 private $api;
22 /** @var array $api */
23 private $config;
24 /**
25 * @param Service $api
26 * @param array $config
27 */
28 public function __construct(Service $api, array $config = [])
29 {
30 $this->api = $api;
31 $this->config = $config;
32 }
33 /**
34 * @param ResultInterface $result
35 * @param CommandInterface|null $command
36 * @param ResponseInterface|null $response
37 *
38 * @return ResultInterface
39 */
40 public function __invoke(ResultInterface $result, ?CommandInterface $command = null, ?ResponseInterface $response = null) : ResultInterface
41 {
42 $operation = $this->api->getOperation($command->getName());
43 // Skip this middleware if the operation doesn't have an httpChecksum
44 $checksumInfo = $operation['httpChecksum'] ?? null;
45 if (\is_null($checksumInfo)) {
46 return $result;
47 }
48 $mode = $this->config['response_checksum_validation'] ?? self::DEFAULT_VALIDATION_MODE;
49 $checksumModeEnabledMember = $checksumInfo['requestValidationModeMember'] ?? "";
50 $checksumModeEnabled = \strtolower($command[$checksumModeEnabledMember] ?? "");
51 $responseAlgorithms = $checksumInfo['responseAlgorithms'] ?? [];
52 $shouldSkipValidation = $this->shouldSkipValidation($mode, $checksumModeEnabled, $responseAlgorithms);
53 if ($shouldSkipValidation) {
54 return $result;
55 }
56 $checksumPriority = $this->getChecksumPriority();
57 $checksumsToCheck = \array_intersect($responseAlgorithms, \array_map('strtoupper', \array_keys($checksumPriority)));
58 $checksumValidationInfo = $this->validateChecksum($checksumsToCheck, $response);
59 if ($checksumValidationInfo['status'] === "SUCCEEDED") {
60 $result['ChecksumValidated'] = $checksumValidationInfo['checksum'];
61 } elseif ($checksumValidationInfo['status'] === "FAILED") {
62 if ($this->isMultipartGetObject($command, $checksumValidationInfo)) {
63 return $result;
64 }
65 throw new S3Exception("Calculated response checksum did not match the expected value", $command);
66 }
67 return $result;
68 }
69 /**
70 * @param $checksumPriority
71 * @param ResponseInterface $response
72 *
73 * @return array
74 */
75 private function validateChecksum($checksumPriority, ResponseInterface $response) : array
76 {
77 $checksumToValidate = $this->chooseChecksumHeaderToValidate($checksumPriority, $response);
78 $validationStatus = "SKIPPED";
79 $checksumHeaderValue = null;
80 if (!empty($checksumToValidate)) {
81 $checksumHeaderValue = $response->getHeaderLine('x-amz-checksum-' . $checksumToValidate);
82 if (!empty($checksumHeaderValue)) {
83 $calculatedChecksumValue = $this->getEncodedValue($checksumToValidate, $response->getBody());
84 $validationStatus = $checksumHeaderValue == $calculatedChecksumValue ? "SUCCEEDED" : "FAILED";
85 }
86 }
87 return ["status" => $validationStatus, "checksum" => $checksumToValidate, "checksumHeaderValue" => $checksumHeaderValue];
88 }
89 /**
90 * @param $checksumPriority
91 * @param ResponseInterface $response
92 *
93 * @return string
94 */
95 private function chooseChecksumHeaderToValidate($checksumPriority, ResponseInterface $response) : ?string
96 {
97 foreach ($checksumPriority as $checksum) {
98 $checksumHeader = 'x-amz-checksum-' . $checksum;
99 if ($response->hasHeader($checksumHeader)) {
100 return $checksum;
101 }
102 }
103 return null;
104 }
105 /**
106 * @param string $mode
107 * @param string $checksumModeEnabled
108 * @param array $responseAlgorithms
109 *
110 * @return bool
111 */
112 private function shouldSkipValidation(string $mode, string $checksumModeEnabled, array $responseAlgorithms) : bool
113 {
114 return empty($responseAlgorithms) || $mode === 'when_required' && $checksumModeEnabled !== 'enabled';
115 }
116 /**
117 * @return string[]
118 */
119 private function getChecksumPriority() : array
120 {
121 return \extension_loaded('awscrt') ? self::$supportedAlgorithms : \array_slice(self::$supportedAlgorithms, 1);
122 }
123 /**
124 * @param CommandInterface $command
125 * @param array $checksumValidationInfo
126 *
127 * @return bool
128 */
129 private function isMultipartGetObject(CommandInterface $command, array $checksumValidationInfo) : bool
130 {
131 if ($command->getName() !== "GetObject" || empty($checksumValidationInfo['checksumHeaderValue'])) {
132 return \false;
133 }
134 $headerValue = $checksumValidationInfo['checksumHeaderValue'];
135 $lastDashPos = \strrpos($headerValue, '-');
136 $endOfChecksum = \substr($headerValue, $lastDashPos + 1);
137 return \is_numeric($endOfChecksum) && (int) $endOfChecksum > 1 && (int) $endOfChecksum < 10000;
138 }
139 }
140