AutorÃa | Ultima modificación | Ver Log |
<?phpnamespace Aws\S3\Parser;use Aws\Api\Service;use Aws\CommandInterface;use Aws\ResultInterface;use Aws\S3\CalculatesChecksumTrait;use Aws\S3\Exception\S3Exception;use Psr\Http\Message\ResponseInterface;/*** A custom s3 result mutator that validates the response checksums.** @internal*/final class ValidateResponseChecksumResultMutator implements S3ResultMutator{use CalculatesChecksumTrait;public const DEFAULT_VALIDATION_MODE = 'when_supported';/** @var Service $api */private $api;/** @var array $api */private $config;/*** @param Service $api* @param array $config*/public function __construct(Service $api, array $config = []){$this->api = $api;$this->config = $config;}/*** @param ResultInterface $result* @param CommandInterface|null $command* @param ResponseInterface|null $response** @return ResultInterface*/public function __invoke(ResultInterface $result,?CommandInterface $command = null,?ResponseInterface $response = null): ResultInterface{$operation = $this->api->getOperation($command->getName());// Skip this middleware if the operation doesn't have an httpChecksum$checksumInfo = $operation['httpChecksum'] ?? null;if (is_null($checksumInfo)) {return $result;}$mode = $this->config['response_checksum_validation'] ?? self::DEFAULT_VALIDATION_MODE;$checksumModeEnabledMember = $checksumInfo['requestValidationModeMember'] ?? "";$checksumModeEnabled = strtolower($command[$checksumModeEnabledMember] ?? "");$responseAlgorithms = $checksumInfo['responseAlgorithms'] ?? [];$shouldSkipValidation = $this->shouldSkipValidation($mode,$checksumModeEnabled,$responseAlgorithms);if ($shouldSkipValidation) {return $result;}$checksumPriority = $this->getChecksumPriority();$checksumsToCheck = array_intersect($responseAlgorithms, array_map('strtoupper',array_keys($checksumPriority)));$checksumValidationInfo = $this->validateChecksum($checksumsToCheck, $response);if ($checksumValidationInfo['status'] === "SUCCEEDED") {$result['ChecksumValidated'] = $checksumValidationInfo['checksum'];} elseif ($checksumValidationInfo['status'] === "FAILED") {if ($this->isMultipartGetObject($command, $checksumValidationInfo)) {return $result;}throw new S3Exception("Calculated response checksum did not match the expected value",$command);}return $result;}/*** @param $checksumPriority* @param ResponseInterface $response** @return array*/private function validateChecksum($checksumPriority,ResponseInterface $response): array{$checksumToValidate = $this->chooseChecksumHeaderToValidate($checksumPriority,$response);$validationStatus = "SKIPPED";$checksumHeaderValue = null;if (!empty($checksumToValidate)) {$checksumHeaderValue = $response->getHeaderLine('x-amz-checksum-' . $checksumToValidate);if (!empty($checksumHeaderValue)) {$calculatedChecksumValue = $this->getEncodedValue($checksumToValidate,$response->getBody());$validationStatus = $checksumHeaderValue == $calculatedChecksumValue? "SUCCEEDED": "FAILED";}}return ["status" => $validationStatus,"checksum" => $checksumToValidate,"checksumHeaderValue" => $checksumHeaderValue,];}/*** @param $checksumPriority* @param ResponseInterface $response** @return string*/private function chooseChecksumHeaderToValidate($checksumPriority,ResponseInterface $response):? string{foreach ($checksumPriority as $checksum) {$checksumHeader = 'x-amz-checksum-' . $checksum;if ($response->hasHeader($checksumHeader)) {return $checksum;}}return null;}/*** @param string $mode* @param string $checksumModeEnabled* @param array $responseAlgorithms** @return bool*/private function shouldSkipValidation(string $mode,string $checksumModeEnabled,array $responseAlgorithms): bool{return empty($responseAlgorithms)|| ($mode === 'when_required' && $checksumModeEnabled !== 'enabled');}/*** @return string[]*/private function getChecksumPriority(): array{return extension_loaded('awscrt')? self::$supportedAlgorithms: array_slice(self::$supportedAlgorithms, 1);}/*** @param CommandInterface $command* @param array $checksumValidationInfo** @return bool*/private function isMultipartGetObject(CommandInterface $command,array $checksumValidationInfo): bool{if ($command->getName() !== "GetObject"|| empty($checksumValidationInfo['checksumHeaderValue'])) {return false;}$headerValue = $checksumValidationInfo['checksumHeaderValue'];$lastDashPos = strrpos($headerValue, '-');$endOfChecksum = substr($headerValue, $lastDashPos + 1);return is_numeric($endOfChecksum)&& (int) $endOfChecksum > 1&& (int) $endOfChecksum < 10000;}}