Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
namespace Aws\S3;
3
 
4
use Aws\Api\Service;
1441 ariadna 5
use Aws\Api\Shape;
1 efrain 6
use Aws\CommandInterface;
1441 ariadna 7
use Aws\MetricsBuilder;
1 efrain 8
use GuzzleHttp\Psr7;
9
use InvalidArgumentException;
10
use Psr\Http\Message\RequestInterface;
1441 ariadna 11
use Psr\Http\Message\StreamInterface;
1 efrain 12
 
13
/**
14
 * Apply required or optional checksums to requests before sending.
15
 *
16
 * IMPORTANT: This middleware must be added after the "build" step.
17
 *
18
 * @internal
19
 */
20
class ApplyChecksumMiddleware
21
{
22
    use CalculatesChecksumTrait;
1441 ariadna 23
 
24
    public const DEFAULT_CALCULATION_MODE = 'when_supported';
25
    public const DEFAULT_ALGORITHM = 'crc32';
26
 
27
    /**
28
     * @var true[]
29
     *
30
     * S3 Operations for which pre-calculated SHA256
31
     * Checksums can be added to the command
32
     */
33
    public static $sha256 = [
34
        'PutObject' => true,
35
        'UploadPart' => true,
1 efrain 36
    ];
37
 
38
    /** @var Service */
39
    private $api;
40
 
1441 ariadna 41
    /** @var array */
42
    private $config;
43
 
44
    /** @var callable */
1 efrain 45
    private $nextHandler;
46
 
47
    /**
48
     * Create a middleware wrapper function.
49
     *
50
     * @param Service $api
51
     * @return callable
52
     */
1441 ariadna 53
    public static function wrap(Service $api, array $config = [])
1 efrain 54
    {
1441 ariadna 55
        return function (callable $handler) use ($api, $config) {
56
            return new self($handler, $api, $config);
1 efrain 57
        };
58
    }
59
 
1441 ariadna 60
    public function __construct(
61
        callable $nextHandler,
62
        Service $api,
63
        array $config = []
64
    )
1 efrain 65
    {
66
        $this->api = $api;
67
        $this->nextHandler = $nextHandler;
1441 ariadna 68
        $this->config = $config;
1 efrain 69
    }
70
 
71
    public function __invoke(
72
        CommandInterface $command,
73
        RequestInterface $request
74
    ) {
75
        $next = $this->nextHandler;
76
        $name = $command->getName();
77
        $body = $request->getBody();
1441 ariadna 78
        $operation = $this->api->getOperation($name);
79
        $mode = $this->config['request_checksum_calculation']
80
            ?? self::DEFAULT_CALCULATION_MODE;
1 efrain 81
 
1441 ariadna 82
        $command->getMetricsBuilder()->identifyMetricByValueAndAppend(
83
            'request_checksum_calculation',
84
            $mode
85
        );
1 efrain 86
 
1441 ariadna 87
        // Trigger warning if AddContentMD5 is specified for PutObject or UploadPart
88
        $this->handleDeprecatedAddContentMD5($command);
1 efrain 89
 
1441 ariadna 90
        $checksumInfo = $operation['httpChecksum'] ?? [];
91
        $checksumMemberName = $checksumInfo['requestAlgorithmMember'] ?? '';
92
        $checksumMember = !empty($checksumMemberName)
93
            ? $operation->getInput()->getMember($checksumMemberName)
1 efrain 94
            : null;
1441 ariadna 95
        $checksumRequired = $checksumInfo['requestChecksumRequired'] ?? false;
96
        $requestedAlgorithm = $command[$checksumMemberName] ?? null;
97
 
98
        $shouldAddChecksum = $this->shouldAddChecksum(
99
            $mode,
100
            $checksumRequired,
101
            $checksumMember,
102
            $requestedAlgorithm
103
        );
104
        if ($shouldAddChecksum) {
105
            if (!$this->hasAlgorithmHeader($request)) {
106
                $supportedAlgorithms =  array_map('strtolower', $checksumMember['enum'] ?? []);
107
                $algorithm = $this->determineChecksumAlgorithm(
108
                    $supportedAlgorithms,
109
                    $requestedAlgorithm,
110
                    $checksumMemberName
1 efrain 111
                );
1441 ariadna 112
                $request = $this->addAlgorithmHeader($algorithm, $request, $body);
1 efrain 113
 
1441 ariadna 114
                $command->getMetricsBuilder()->identifyMetricByValueAndAppend(
115
                    'request_checksum',
116
                    $algorithm
1 efrain 117
                );
118
            }
119
        }
120
 
1441 ariadna 121
        // Set the content hash header if ContentSHA256 is provided
122
        if (isset(self::$sha256[$name]) && $command['ContentSHA256']) {
1 efrain 123
            $request = $request->withHeader(
124
                'X-Amz-Content-Sha256',
125
                $command['ContentSHA256']
126
            );
1441 ariadna 127
            $command->getMetricsBuilder()->append(
128
                MetricsBuilder::FLEXIBLE_CHECKSUMS_REQ_SHA256
129
            );
1 efrain 130
        }
131
 
132
        return $next($command, $request);
133
    }
1441 ariadna 134
 
135
    /**
136
     * @param CommandInterface $command
137
     *
138
     * @return void
139
     */
140
    private function handleDeprecatedAddContentMD5(CommandInterface $command): void
141
    {
142
        if (!empty($command['AddContentMD5'])) {
143
            trigger_error(
144
                'S3 no longer supports MD5 checksums. ' .
145
                'A CRC32 checksum will be computed and applied on your behalf.',
146
                E_USER_DEPRECATED
147
            );
148
            $command['ChecksumAlgorithm'] = self::DEFAULT_ALGORITHM;
149
        }
150
    }
151
 
152
    /**
153
     * @param string $mode
154
     * @param Shape|null $checksumMember
155
     * @param string $name
156
     * @param bool $checksumRequired
157
     * @param string|null $requestedAlgorithm
158
     *
159
     * @return bool
160
     */
161
    private function shouldAddChecksum(
162
        string $mode,
163
        bool $checksumRequired,
164
        ?Shape $checksumMember,
165
        ?string $requestedAlgorithm
166
    ): bool
167
    {
168
        return ($mode === 'when_supported' && $checksumMember)
169
            || ($mode === 'when_required'
170
                && ($checksumRequired || ($checksumMember && $requestedAlgorithm)));
171
    }
172
 
173
    /**
174
     * @param Shape|null $checksumMember
175
     * @param string|null $requestedAlgorithm
176
     * @param string|null $checksumMemberName
177
     *
178
     * @return string
179
     */
180
    private function determineChecksumAlgorithm(
181
        array $supportedAlgorithms,
182
        ?string $requestedAlgorithm,
183
        ?string $checksumMemberName
184
    ): string
185
    {
186
        $algorithm = self::DEFAULT_ALGORITHM;
187
 
188
        if ($requestedAlgorithm) {
189
            $requestedAlgorithm = strtolower($requestedAlgorithm);
190
            if (!in_array($requestedAlgorithm, $supportedAlgorithms)) {
191
                throw new InvalidArgumentException(
192
                    "Unsupported algorithm supplied for input variable {$checksumMemberName}. " .
193
                    "Supported checksums for this operation include: "
194
                    . implode(", ", $supportedAlgorithms) . "."
195
                );
196
            }
197
            $algorithm = $requestedAlgorithm;
198
        }
199
 
200
        return $algorithm;
201
    }
202
 
203
    /**
204
     * @param string $requestedAlgorithm
205
     * @param RequestInterface $request
206
     * @param StreamInterface $body
207
     *
208
     * @return RequestInterface
209
     */
210
    private function addAlgorithmHeader(
211
        string $requestedAlgorithm,
212
        RequestInterface $request,
213
        StreamInterface $body
214
    ): RequestInterface
215
    {
216
        $headerName = "x-amz-checksum-{$requestedAlgorithm}";
217
        if (!$request->hasHeader($headerName)) {
218
            $encoded = self::getEncodedValue($requestedAlgorithm, $body);
219
            $request = $request->withHeader($headerName, $encoded);
220
        }
221
 
222
        return $request;
223
    }
224
 
225
    /**
226
     * @param RequestInterface $request
227
     *
228
     * @return bool
229
     */
230
    private function hasAlgorithmHeader(RequestInterface $request): bool
231
    {
232
        $headers = $request->getHeaders();
233
 
234
        foreach ($headers as $name => $values) {
235
            if (stripos($name, 'x-amz-checksum-') === 0) {
236
                return true;
237
            }
238
        }
239
 
240
        return false;
241
    }
1 efrain 242
}