PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / app / Services / FileSystem / Drivers / S3 / S3FileDeleter.php

S3FileDeleter.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Services/FileSystem/Drivers/S3/S3FileDeleter.php

147 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\FileSystem\Drivers\S3;
4
5 use Exception;
6 use FluentCart\App\Modules\StorageDrivers\S3\S3;
7
8 class S3FileDeleter
9 {
10 private string $accessKey;
11 private string $secretKey;
12 private string $bucket;
13 private string $region;
14 private string $hashAlgorithm = 'sha256';
15 private string $httpMethod;
16 private string $s3FilePath;
17 private string $signature;
18 private string $requestUrl;
19 private $timeStamp;
20 private $date;
21
22 public function __construct(string $secret, string $accessKey, string $bucket, string $region, string $s3FilePath)
23 {
24 $this->secretKey = $secret;
25 $this->accessKey = $accessKey;
26 $this->bucket = $bucket;
27 $this->region = S3::getBucketRegion($bucket);
28 $this->s3FilePath = $s3FilePath;
29 $this->httpMethod = "DELETE";
30 $this->timeStamp = gmdate('Ymd\THis\Z');
31 $this->date = substr($this->timeStamp, 0, 8);
32 $hasDot = strpos($this->bucket, '.') !== false;
33 $encodedFilePath = $this->encodeS3ObjectKey($this->s3FilePath);
34 if ($hasDot) {
35 $this->requestUrl = "https://s3.{$this->region}.amazonaws.com/{$this->bucket}/{$encodedFilePath}";
36 } else {
37 $this->requestUrl = "https://{$this->bucket}.s3.{$this->region}.amazonaws.com/{$encodedFilePath}";
38 }
39 $this->signature = $this->generateSignature();
40 }
41
42 public static function delete(string $secret, string $accessKey, string $bucket, string $region, string $s3FilePath)
43 {
44 return (new static($secret, $accessKey, $bucket, $region, $s3FilePath))->deleteFile();
45 }
46
47 public function deleteFile()
48 {
49 add_filter('http_request_timeout', function () {
50 return 30;
51 });
52
53 $response = wp_remote_request($this->requestUrl, [
54 'method' => $this->httpMethod,
55 'headers' => $this->getHeaders()
56 ]);
57
58 $responseCode = wp_remote_retrieve_response_code($response);
59
60 if ($responseCode == 204) {
61 return [
62 'message' => __('File Deleted Successfully', 'fluent-cart'),
63 'driver' => 's3',
64 'path' => $this->s3FilePath
65 ];
66 } else {
67 return new \WP_Error($responseCode, __('Failed To Delete File', 'fluent-cart'));
68 }
69 }
70
71 private function generateSignature()
72 {
73 return hash_hmac(
74 $this->hashAlgorithm,
75 $this->createStringToSign(),
76 $this->getSigningKey()
77 );
78 }
79
80 private function createStringToSign(): string
81 {
82 $hash = hash($this->hashAlgorithm, $this->createCanonicalUrl());
83 return "AWS4-HMAC-SHA256\n{$this->timeStamp}\n{$this->getScope()}\n{$hash}";
84 }
85
86 private function createCanonicalUrl(): string
87 {
88 $s3FilePath = '/' . $this->encodeS3ObjectKey($this->s3FilePath);
89 $hasDot = strpos($this->bucket, '.') !== false;
90 $canonicalUri = $hasDot ? '/' . $this->bucket . $s3FilePath : $s3FilePath;
91
92 return "{$this->httpMethod}\n{$canonicalUri}\n\nhost:{$this->getHost()}\nx-amz-content-sha256:{$this->getContentHash()}\nx-amz-date:{$this->timeStamp}\n\nhost;x-amz-content-sha256;x-amz-date\n{$this->getContentHash()}";
93 }
94
95 /**
96 * Percent-encodes each "/"-separated segment of an S3 object key using
97 * AWS's UriEncode rules (rawurlencode leaves "/" alone). This keeps the
98 * canonical signing path and the actual request URL identical to what
99 * S3 receives on the wire, so multi-byte UTF-8 characters, spaces, and
100 * reserved characters ("+", "#", "?", literal "%") all round-trip to
101 * the exact key that was requested instead of a different object.
102 *
103 * Deliberately does not ltrim() leading slashes: "foo", "/foo", and
104 * "//foo" are three distinct S3 keys, and stripping the slash made a
105 * delete/upload targeting "/foo" silently operate on "foo" instead.
106 */
107 private function encodeS3ObjectKey(string $path): string
108 {
109 return implode('/', array_map('rawurlencode', explode('/', $path)));
110 }
111
112 private function getHost(): string
113 {
114 if (strpos($this->bucket, '.') !== false) {
115 return "s3.{$this->region}.amazonaws.com";
116 }
117
118 return "{$this->bucket}.s3.{$this->region}.amazonaws.com";
119 }
120
121 private function getContentHash(): string
122 {
123 return hash($this->hashAlgorithm, "");
124 }
125
126 private function getScope(): string
127 {
128 return "{$this->date}/{$this->region}/s3/aws4_request";
129 }
130
131 private function getSigningKey()
132 {
133 $dateKey = hash_hmac($this->hashAlgorithm, $this->date, "AWS4{$this->secretKey}", true);
134 $regionKey = hash_hmac($this->hashAlgorithm, $this->region, $dateKey, true);
135 $serviceKey = hash_hmac($this->hashAlgorithm, 's3', $regionKey, true);
136 return hash_hmac($this->hashAlgorithm, 'aws4_request', $serviceKey, true);
137 }
138
139 private function getHeaders(): array
140 {
141 return [
142 'x-amz-content-sha256' => $this->getContentHash(),
143 'x-amz-date' => $this->timeStamp,
144 'Authorization' => "AWS4-HMAC-SHA256 Credential={$this->accessKey}/{$this->date}/{$this->region}/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature={$this->signature}"
145 ];
146 }
147 }