PluginProbe
Media Cloud Sync / 1.3.0
Media Cloud Sync v1.3.0
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 / base / services / s3.php

s3.php in Media Cloud Sync 1.3.0, at includes/base/services/s3.php

1,117 lines 44.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Dudlewebs\WPMCS;
3
4 defined('ABSPATH') || exit;
5
6 // Libraries
7 use Dudlewebs\WPMCS\s3\Aws\S3\S3Client;
8 use Dudlewebs\WPMCS\s3\Aws\Exception\AwsException;
9 use Dudlewebs\WPMCS\s3\Aws\S3\Exception\S3Exception;
10 use Dudlewebs\WPMCS\s3\Aws\S3\MultipartUploader;
11 use Dudlewebs\WPMCS\s3\Aws\Exception\MultipartUploadException;
12 use Exception;
13
14 class S3 {
15 private $assets_url;
16 private $version;
17 private $token;
18
19 protected $config;
20 protected $bucketConfig;
21 protected $settings;
22 protected $credentials;
23 protected $bucket_name;
24 protected $cdnConfig;
25
26 public $service = 's3';
27 public $s3Client = false;
28
29 /**
30 * Admin constructor.
31 * @since 1.0.0
32 */
33 public function __construct() {
34 $this->assets_url = WPMCS_ASSETS_URL;
35 $this->version = WPMCS_VERSION;
36 $this->token = WPMCS_TOKEN;
37
38 // Initialize setup
39 $this->init();
40 }
41
42 /**
43 * Initialise Client
44 */
45 public function init() {
46 $this->settings = Utils::get_settings();
47 $this->credentials = Utils::get_credentials();
48 $this->config = isset($this->credentials['config']) && !empty($this->credentials['config'])
49 ? $this->credentials['config']
50 : [];
51 $this->bucketConfig = isset($this->credentials['bucketConfig']) && !empty($this->credentials['bucketConfig'])
52 ? $this->credentials['bucketConfig']
53 : [];
54 $this->bucket_name = isset($this->bucketConfig['bucket_name']) && !empty($this->bucketConfig['bucket_name'])
55 ? $this->bucketConfig['bucket_name']
56 : '';
57 $this->cdnConfig = isset($this->credentials['cdn']) && !empty($this->credentials['cdn'])
58 ? $this->credentials['cdn']
59 : [];
60
61 if (
62 isset($this->config['region']) && !empty($this->config['region']) &&
63 isset($this->config['access_key']) && !empty($this->config['access_key']) &&
64 isset($this->config['secret_key']) && !empty($this->config['secret_key'])
65 ) {
66 $this->s3Client = new S3Client([
67 'version' => '2006-03-01',
68 'region' => $this->config['region'],
69 'use_accelerate_endpoint' => isset($this->bucketConfig['transfer_acceleration'])
70 ? $this->bucketConfig['transfer_acceleration'] : false,
71 'use_aws_shared_config_files' => false,
72 'credentials' => [
73 'key' => $this->config['access_key'],
74 'secret' => $this->config['secret_key'],
75 ],
76 ]);
77 }
78
79 }
80
81
82 /**
83 * Verify Credentials
84 * @since 1.0.0
85 * @return boolean
86 */
87 public function verifyCredentials($access_key, $secret_key, $region){
88 if (
89 isset($region) && !empty($region) &&
90 isset($access_key) && !empty($access_key) &&
91 isset($secret_key) && !empty($secret_key)
92 ) {
93 try {
94 $s3Client = new S3Client([
95 'version' => '2006-03-01',
96 'region' => $region,
97 'use_aws_shared_config_files' => false,
98 'credentials' => [
99 'key' => $access_key,
100 'secret' => $secret_key,
101 ],
102 ]);
103
104 $result = [
105 'success' => false,
106 'code' => 200,
107 'message' => esc_html__('Please check the authorization details', 'media-cloud-sync'),
108 ];
109
110 try {
111 $s3Client->listObjectsV2([
112 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
113 ]);
114
115 // If we reach here, the credentials are valid
116 $result = [
117 'success' => true,
118 'code' => 200,
119 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
120 ];
121 } catch (AwsException $e) {
122 $code = $e->getAwsErrorCode();
123
124 $validErrors = [
125 'AccessDenied',
126 'NoSuchBucket',
127 'AllAccessDisabled',
128 'AuthorizationHeaderMalformed',
129 'PermanentRedirect',
130 'InvalidBucketName',
131 ];
132
133 if (in_array($code, $validErrors)) {
134 // If we reach here, the credentials are valid
135 $result = [
136 'success' => true,
137 'code' => 200,
138 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'),
139 ];
140 }
141 }
142
143 if($result['success'] == false) {
144 return $result;
145 }
146
147 try {
148 $buckets = $s3Client->listBuckets();
149 $newBucketFormat = [];
150 if(isset($buckets['Buckets']) && !empty($buckets['Buckets'])){
151 foreach($buckets['Buckets'] as $bucket) {
152 if(isset($bucket['Name'])) {
153 $newBucketFormat[] = ['Name' => $bucket['Name'], 'CreationDate' => $bucket['CreationDate']];
154 }
155 }
156 }
157 $result['buckets_data']['buckets'] = $newBucketFormat;
158 $result['buckets_data']['message'] = esc_html__('Buckets listed successfully', 'media-cloud-sync');
159 $result['buckets_data']['status'] = true;
160 } catch (S3Exception $e) {
161 $result ['buckets_data']['buckets'] = [];
162 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
163 $result ['buckets_data']['status'] = false;
164 } catch (Exception $e) {
165 $result ['buckets_data']['buckets'] = [];
166 $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync');
167 $result ['buckets_data']['status'] = false;
168 }
169 return $result;
170 }
171 catch (S3Exception $ex) {
172 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
173 } catch (Exception $ex) {
174 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
175 }
176 }
177 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
178 }
179
180 /**
181 * Verify Bucket Exist
182 * @since 1.0.0
183 * @return boolean
184 */
185 public function verifyBucketExist($access_key, $secret_key, $region, $bucket_name, $transfer_acceleration=false){
186 if (
187 isset($region) && !empty($region) &&
188 isset($access_key) && !empty($access_key) &&
189 isset($secret_key) && !empty($secret_key) &&
190 isset($bucket_name) && !empty($bucket_name)
191 ) {
192 try {
193 $s3Client = new S3Client([
194 'version' => '2006-03-01',
195 'region' => $region,
196 'use_accelerate_endpoint' => $transfer_acceleration,
197 'use_aws_shared_config_files' => false,
198 'credentials' => [
199 'key' => $access_key,
200 'secret' => $secret_key,
201 ],
202 ]);
203
204 //get S3 object
205 $bucket_found = false;
206 try {
207 $s3Client->getObject([
208 'Bucket' => $bucket_name,
209 'Key' => $this->token . '_dummy-object-for-bucket-exist-check'
210 ]);
211 $bucket_found = true;
212 } catch (AwsException $e) {
213 $code = $e->getAwsErrorCode();
214 if ($code === 'NoSuchKey') {
215 $bucket_found = true;
216 }
217 }
218
219 if($bucket_found) {
220 return array('message' => esc_html__('Bucket exist', 'media-cloud-sync'), 'code' => 200, 'success' => true);
221 } else {
222 return array('message' => esc_html__("Bucket choosen does not exist / does not have read permission", 'media-cloud-sync'), 'code' => 200, 'success' => false);
223 }
224 }
225 catch (S3Exception $ex) {
226 return array('message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
227 } catch (Exception $ex) {
228 return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false);
229 }
230 }
231 return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false);
232 }
233
234 /**
235 * Create Bucket
236 * @since 1.0.0
237 * @return boolean
238 */
239 public function createBucket($access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
240 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
241 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
242 }
243
244 try {
245 $s3ClientConfig = [
246 'version' => '2006-03-01',
247 'region' => $region,
248 'use_aws_shared_config_files' => false,
249 'credentials' => [
250 'key' => $access_key,
251 'secret' => $secret_key,
252 ],
253 ];
254
255 $s3Client = new S3Client($s3ClientConfig);
256
257 // Create Bucket
258 if ($region === 'us-east-1') {
259 $s3Client->createBucket([
260 'Bucket' => $bucket_name,
261 ]);
262 } else {
263 $s3Client->createBucket([
264 'Bucket' => $bucket_name,
265 'CreateBucketConfiguration' => [
266 'LocationConstraint' => $region,
267 ],
268 ]);
269 }
270
271 try {
272 $this->blockPublicAccess($bucket_name, false, $s3Client);
273 $this->putBucketPolicy($bucket_name, $s3Client);
274 $this->changeBucketOwnership($bucket_name, $s3Client);
275 try {
276
277 $this->changeTransferAccilaration($bucket_name, $s3Client, $transfer_acceleration);
278
279 return [
280 'message' => esc_html__('Bucket created successfully. Choose bucket from list to select the bucket.', 'media-cloud-sync'),
281 'data' => [
282 'Name' => $bucket_name,
283 'CreationDate' => date('Y-m-d\TH:i:s\Z'),
284 ],
285 'code' => 200,
286 'success' => true,
287 ];
288 } catch (AwsException $ex) {
289 return ['message' => esc_html__('Bucket created and made public. But the following error happened while setting the transfer accilaration,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
290 }
291 } catch (AwsException $ex) {
292 return ['message' => esc_html__('Bucket created. But the following error happened while setting the public access,', 'media-cloud-sync') . ' ' . $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
293 }
294 } catch (AwsException $ex) {
295 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
296 } catch (S3Exception $ex) {
297 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
298 } catch (Exception $ex) {
299 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
300 }
301 }
302
303 /**
304 * Check Bucket Write Permission
305 * @since 1.0.0
306 */
307 public function verifyObjectWritePermission($access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
308 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
309 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
310 }
311
312 try {
313 $s3ClientConfig = [
314 'version' => '2006-03-01',
315 'region' => $region,
316 'use_accelerate_endpoint' => $transfer_acceleration,
317 'use_aws_shared_config_files' => false,
318 'credentials' => [
319 'key' => $access_key,
320 'secret' => $secret_key,
321 ],
322 ];
323
324 $s3Client = new S3Client($s3ClientConfig);
325
326 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
327
328
329 // Create a dummy object to check write permission
330 $s3Client->putObject([
331 'Bucket' => $bucket_name,
332 'Key' => $object_key,
333 'Body' => 'This is a test object to check write permission.',
334 ]);
335 // Check if the object was created successfully
336 if ($s3Client->doesObjectExist($bucket_name, $object_key)) {
337 return ['message' => esc_html__('Bucket write permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
338 } else {
339 return ['message' => esc_html__('Bucket write permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
340 }
341
342 } catch (AwsException $ex) {
343 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
344 } catch (S3Exception $ex) {
345 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
346 } catch (Exception $ex) {
347 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
348 }
349 }
350
351
352
353 /**
354 * Check Bucket Delete Permission
355 * @since 1.0.0
356 */
357 public function verifyObjectDeletePermission($access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
358 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
359 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
360 }
361
362 try {
363 $s3ClientConfig = [
364 'version' => '2006-03-01',
365 'region' => $region,
366 'use_accelerate_endpoint' => $transfer_acceleration,
367 'use_aws_shared_config_files' => false,
368 'credentials' => [
369 'key' => $access_key,
370 'secret' => $secret_key,
371 ],
372 ];
373
374 $s3Client = new S3Client($s3ClientConfig);
375
376 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
377
378 // Create a dummy object to check dlete permission
379 $s3Client->deleteObject([
380 'Bucket' => $bucket_name,
381 'Key' => $object_key,
382 ]);
383
384 // Check if the object was created successfully
385 if (!$s3Client->doesObjectExist($bucket_name, $object_key)) {
386 return ['message' => esc_html__('Bucket delete permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true];
387 } else {
388 return ['message' => esc_html__('Bucket delete permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false];
389 }
390
391 } catch (AwsException $ex) {
392 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
393 } catch (S3Exception $ex) {
394 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
395 } catch (Exception $ex) {
396 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
397 }
398 }
399
400
401 /**
402 * Check Bucket Read Permission
403 * @since 1.2.4
404 */
405 public function verifyObjectReadPermission() {
406 if (empty($this->s3Client) || empty($this->bucket_name)) {
407 return ['message' => esc_html__('Invalid Request', 'media-cloud-sync'), 'code' => 200, 'success' => false];
408 }
409
410 try {
411 $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', '');
412
413 // Check if the object was created successfully
414 if (!$this->s3Client->doesObjectExist($this->bucket_name, $object_key)) {
415 // Create a dummy object to check write permission
416 $this->s3Client->putObject([
417 'Bucket' => $this->bucket_name,
418 'Key' => $object_key,
419 'Body' => 'This is a test object to check permission.',
420 ]);
421 }
422
423
424 $url = $this->generate_file_url($object_key);
425 $cdn_url = Cdn::may_generate_cdn_url($url, $object_key);
426 $headers = @get_headers($cdn_url);
427 $result = [
428 'status' => false,
429 'message' => '',
430 'lastChecked' => time(),
431 ];
432 if (strpos($headers[0], '200') !== false) {
433 $result['status'] = true;
434 $result['message'] = esc_html__('Objects are accessible to Read', 'media-cloud-sync');
435 } else if (strpos($headers[0], '403') !== false) {
436 $result['status'] = false;
437 if($this->cdnConfig['service'] == $this->service) {
438 $result['message'] = esc_html__('Access Denied. Please check your bucket policy. Public Read Access is required.', 'media-cloud-sync');
439 } else {
440 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
441 }
442 $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync');
443 } else if (strpos($headers[0], '404') !== false) {
444 $result['status'] = false;
445 $result['message'] = esc_html__('Object not found. Please check your bucket policy', 'media-cloud-sync');
446 } else if (strpos($headers[0], '500') !== false) {
447 $result['status'] = false;
448 $result['message'] = esc_html__('Internal Server error. Please check your bucket policy', 'media-cloud-sync');
449 } else {
450 $result['status'] = false;
451 $result['message'] = esc_html__('Objects are not accessible to read', 'media-cloud-sync');
452 }
453 Utils::set_status('cdnRead', $result);
454
455 $this->deleteSingle($object_key);
456 return [
457 'message' => $result['message'],
458 'code' => 200,
459 'success' => $result['status'],
460 'lastChecked' => $result['lastChecked'],
461 ];
462 } catch (AwsException $ex) {
463 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
464 } catch (S3Exception $ex) {
465 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
466 } catch (Exception $ex) {
467 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
468 }
469 }
470
471
472 /**
473 * get Bucket Security Settings
474 */
475 public function getBucketSecuritySettings($access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
476 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
477 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
478 }
479
480 $errors = [];
481 try {
482 $s3ClientConfig = [
483 'version' => '2006-03-01',
484 'region' => $region,
485 'use_accelerate_endpoint' => $transfer_acceleration,
486 'use_aws_shared_config_files' => false,
487 'credentials' => [
488 'key' => $access_key,
489 'secret' => $secret_key,
490 ],
491 ];
492
493 $s3Client = new S3Client($s3ClientConfig);
494
495 // Check public access block configuration
496 $security['block_public_access'] = false;
497 try {
498 $publicAccessBlock = $s3Client->getPublicAccessBlock([
499 'Bucket' => $bucket_name,
500 ]);
501
502 $publicAccessBlockConfig = $publicAccessBlock['PublicAccessBlockConfiguration'];
503 $security = [];
504 if (
505 $publicAccessBlockConfig['BlockPublicAcls'] &&
506 $publicAccessBlockConfig['IgnorePublicAcls'] &&
507 $publicAccessBlockConfig['BlockPublicPolicy'] &&
508 $publicAccessBlockConfig['RestrictPublicBuckets']
509 ) {
510 $security['block_public_access'] = true;
511 }
512 } catch (S3Exception $ex) {
513 // If the bucket does not have public access block configuration, we assume it is not blocked
514 $errors['block_public_access'] = $ex->getMessage();
515 } catch (Exception $ex) {
516 $errors['block_public_access'] = $ex->getMessage();
517 }
518
519
520 $security['object_ownership_enforced'] = false;
521 try {
522 $ownershipControls = $s3Client->getBucketOwnershipControls([
523 'Bucket' => $bucket_name,
524 ]);
525
526 $ownershipRule = $ownershipControls['OwnershipControls']['Rules'][0]['ObjectOwnership'];
527
528 if ($ownershipRule === 'BucketOwnerEnforced') {
529 $security['object_ownership_enforced'] = true;
530 }
531 } catch (S3Exception $ex) {
532 $errors['object_ownership_enforced'] = $ex->getMessage();
533 } catch (Exception $ex) {
534 $errors['object_ownership_enforced'] = $ex->getMessage();
535 }
536
537 return ['message' => '', 'code' => 200, 'success' => empty($errors), 'security' => $security, 'errors' => $errors];
538 } catch (AwsException $ex) {
539 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
540 } catch (S3Exception $ex) {
541 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
542 } catch (Exception $ex) {
543 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
544 }
545 }
546
547
548
549 /**
550 * Change Bucket Public Access
551 */
552
553 public function changePublicAccess($value, $access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
554 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
555 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
556 }
557
558 try {
559 $s3ClientConfig = [
560 'version' => '2006-03-01',
561 'region' => $region,
562 'use_accelerate_endpoint' => $transfer_acceleration,
563 'use_aws_shared_config_files' => false,
564 'credentials' => [
565 'key' => $access_key,
566 'secret' => $secret_key,
567 ],
568 ];
569
570 $s3Client = new S3Client($s3ClientConfig);
571
572 try {
573 $result = $this->blockPublicAccess($bucket_name, $value, $s3Client);
574 return ['message' => '', 'code' => 200, 'success' => true, 'result' => $result];
575 } catch (AwsException $ex) {
576 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
577 } catch (S3Exception $ex) {
578 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
579 } catch (Exception $ex) {
580 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
581 }
582 } catch (AwsException $ex) {
583 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
584 } catch (S3Exception $ex) {
585 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
586 } catch (Exception $ex) {
587 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
588 }
589 }
590
591
592
593 /**
594 * Change Bucket Ownership
595 */
596
597 public function changeObjectOwnership($value, $access_key, $secret_key, $region, $bucket_name, $transfer_acceleration = false){
598 if (empty($region) || empty($access_key) || empty($secret_key) || empty($bucket_name)) {
599 return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false];
600 }
601
602 try {
603 $s3ClientConfig = [
604 'version' => '2006-03-01',
605 'region' => $region,
606 'use_accelerate_endpoint' => $transfer_acceleration,
607 'use_aws_shared_config_files' => false,
608 'credentials' => [
609 'key' => $access_key,
610 'secret' => $secret_key,
611 ],
612 ];
613
614 $s3Client = new S3Client($s3ClientConfig);
615
616 $ownership = $value ? 'BucketOwnerEnforced' : 'BucketOwnerPreferred';
617
618 try {
619 $result = $this->changeBucketOwnership( $bucket_name, $s3Client, $ownership );
620 return ['message' => '', 'code' => 200, 'success' => true, 'result' => $result];
621 } catch (AwsException $ex) {
622 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
623 } catch (S3Exception $ex) {
624 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
625 } catch (Exception $ex) {
626 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
627 }
628
629 }
630 catch (AwsException $ex) {
631 return ['message' => $ex->getAwsErrorMessage(), 'code' => 200, 'success' => false];
632 } catch (S3Exception $ex) {
633 return ['message' => $ex->getAwsErrorMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
634 } catch (Exception $ex) {
635 return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false];
636 }
637 }
638
639
640
641
642 /**
643 * Block Public Access
644 * @since 1.0.0
645 */
646 private function blockPublicAccess($bucket, $block = true, $s3Client = false) {
647 if($s3Client == false) {
648 $s3Client = $this->s3Client;
649 }
650
651 if(empty($bucket)) return false;
652
653 try {
654 $s3Client->putPublicAccessBlock([
655 'Bucket' => $bucket,
656 'PublicAccessBlockConfiguration' => [
657 'BlockPublicPolicy' => $block,
658 'BlockPublicAcls' => $block,
659 'IgnorePublicAcls' => $block,
660 'RestrictPublicBuckets' => $block,
661 ]
662 ]);
663
664 return true;
665 } catch (AwsException $ex) {
666 return false;
667 }
668 }
669
670 /**
671 * Add Bucket Policy
672 */
673 private function putBucketPolicy($bucket, $s3Client = false) {
674 if($s3Client == false) {
675 $s3Client = $this->s3Client;
676 }
677
678 if(empty($bucket)) return false;
679
680 $policy = json_encode([
681 "Version" => "2012-10-17",
682 "Statement" => [
683 [
684 "Effect" => "Allow",
685 "Principal" => "*",
686 "Action" => [
687 "s3:DeleteObjectTagging",
688 "s3:ListBucketMultipartUploads",
689 "s3:DeleteObjectVersion",
690 "s3:ListBucket",
691 "s3:DeleteObjectVersionTagging",
692 "s3:GetBucketAcl",
693 "s3:ListMultipartUploadParts",
694 "s3:PutObject",
695 "s3:GetObjectAcl",
696 "s3:GetObject",
697 "s3:AbortMultipartUpload",
698 "s3:DeleteObject",
699 "s3:GetBucketLocation",
700 "s3:PutObjectAcl",
701 "s3:putBucketOwnershipControls",
702 "s3:putBucketPolicy"
703 ],
704 "Resource" => [
705 "arn:aws:s3:::$bucket/*",
706 "arn:aws:s3:::$bucket"
707 ]
708 ]
709 ]
710 ]);
711
712 try {
713 // Add bucket policy
714 $s3Client->putBucketPolicy(['Bucket' => $bucket, 'Policy' => $policy]);
715
716 return true;
717 } catch (AwsException $ex) {
718 return false;
719 } catch (S3Exception $ex) {
720 return false;
721 } catch (Exception $ex) {
722 return false;
723 }
724 }
725
726 /**
727 * Add Bucket Ownership
728 */
729 private function changeBucketOwnership($bucket, $s3Client = false, $ownership = 'BucketOwnerPreferred') {
730 if($s3Client == false) {
731 $s3Client = $this->s3Client;
732 }
733
734 if(empty($bucket)) return false;
735
736 try {
737 // Change object ownership ACL enabled
738 $s3Client->putBucketOwnershipControls([
739 'Bucket' => $bucket,
740 'OwnershipControls' => [
741 'Rules' => [['ObjectOwnership' => $ownership]],
742 ],
743 ]);
744
745 return true;
746 } catch (AwsException $ex) {
747 return false;
748 } catch (S3Exception $ex) {
749 return false;
750 } catch (Exception $ex) {
751 return false;
752 }
753 }
754
755 /**
756 * Change transfer accilaration
757 */
758 private function changeTransferAccilaration($bucket, $s3Client = false, $enable=false, $force = false) {
759 if($s3Client == false) {
760 $s3Client = $this->s3Client;
761 }
762
763 if(empty($bucket)) return false;
764 if(!$force && !$enable) return true;
765
766 // Check if the bucket already has transfer acceleration enabled
767 try {
768 $s3Client->putBucketAccelerateConfiguration([
769 'Bucket' => $bucket,
770 'AccelerateConfiguration' => [
771 'Status' => $enable ? 'Enabled' : 'Suspended'
772 ]
773 ]);
774 return true;
775 } catch (AwsException $ex) {
776 return false;
777 } catch (S3Exception $ex) {
778 return false;
779 } catch (Exception $ex) {
780 return false;
781 }
782 }
783
784 /**
785 * isConfigured Function To Identify the congfigurations are correct
786 * @since 1.0.0
787 */
788 public function isConfigured(){
789 if ($this->s3Client) {
790 try {
791 $this->s3Client->listObjectsV2([
792 'Bucket' => $this->token . '_dummy-bucket-for-auth-check'
793 ]);
794
795 // If we reach here, the credentials are valid
796 return true;
797 } catch (AwsException $ex) {
798 $code = $e->getAwsErrorCode();
799
800 $validErrors = [
801 'AccessDenied',
802 'NoSuchBucket',
803 'AllAccessDisabled',
804 'AuthorizationHeaderMalformed',
805 'PermanentRedirect',
806 'InvalidBucketName'
807 ];
808
809 if (in_array($code, $validErrors)) {
810 // If we reach here, the credentials are valid
811 return true;
812 } else {
813 return false;
814 }
815 } catch (S3Exception $ex) {
816 return false;
817 } catch (Exception $ex) {
818 return false;
819 }
820 }
821 return false;
822 }
823
824 /**
825 * Make Object Private
826 * @since 1.0.0
827 *
828 */
829 public function toPrivate($key) {
830 if(!$key) return false;
831 try {
832 $this->s3Client->putObjectAcl([
833 'Bucket' => $this->bucket_name,
834 'Key' => $key,
835 'ACL' => 'private'
836 ]);
837 return true;
838 } catch (AwsException $ex) {
839 return false;
840 }
841 return false;
842 }
843
844
845
846 /**
847 * Make Object Public
848 * @since 1.0.0
849 *
850 */
851 public function toPublic($key) {
852 if(!$key) return false;
853 try {
854 $this->s3Client->putObjectAcl([
855 'Bucket' => $this->bucket_name,
856 'Key' => $key,
857 'ACL' => 'public-read'
858 ]);
859 return true;
860 } catch (AwsException $ex) {
861 return false;
862 }
863 return false;
864 }
865
866
867
868 /**
869 * Check the object exist
870 * @since 1.1.8
871 */
872 public function exists($key) {
873 if(!$key) return false;
874 try {
875 if($this->s3Client->doesObjectExist($this->bucket_name, $key)) {
876 return true;
877 }
878 } catch (AwsException $ex) {
879 return false;
880 } catch (S3Exception $ex) {
881 return false;
882 } catch (Exception $ex) {
883 return false;
884 }
885 }
886
887 /**
888 * Upload Single
889 * @since 1.0.0
890 * @return boolean
891 */
892 public function uploadSingle($media_absolute_path, $media_path, $prefix='') {
893 $result = array();
894 if (
895 isset($media_absolute_path) && !empty($media_absolute_path) &&
896 isset($media_path) && !empty($media_path)
897 ) {
898 $file_name = wp_basename( $media_path );
899 if ($file_name) {
900 $upload_path = Utils::generate_object_key($media_path, $prefix);
901
902 // Decide Multipart upload or normal put object
903 if (filesize($media_absolute_path) <= Schema::getConstant('S3_MULTIPART_MIN_FILE_SIZE')) {
904 // Upload a publicly accessible file. The file size and type are determined by the SDK.
905 try {
906 $upload = $this->s3Client->putObject([
907 'Bucket' => $this->bucket_name,
908 'Key' => $upload_path,
909 'Body' => fopen($media_absolute_path, 'r'),
910 ]);
911
912 $result = array(
913 'success' => true,
914 'code' => 200,
915 'file_url' => $this->generate_file_url($upload_path),
916 'key' => $upload_path,
917 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
918 );
919 } catch (AwsException $e) {
920 $result = array(
921 'success' => false,
922 'code' => 200,
923 'message' => $e->getMessage()
924 );
925 }
926 } else {
927 $multiUploader = new MultipartUploader($this->s3Client, $media_absolute_path, [
928 'bucket' => $this->bucket_name,
929 'key' => $upload_path,
930 ]);
931
932 try {
933 do {
934 try {
935 $uploaded = $multiUploader->upload();
936 } catch (MultipartUploadException $e) {
937 $multiUploader = new MultipartUploader($this->s3Client, $media_absolute_path, [
938 'state' => $e->getState(),
939 ]);
940 }
941 } while (!isset($uploaded));
942
943 if (isset($uploaded['ObjectURL']) && !empty($uploaded['ObjectURL'])) {
944 $result = array(
945 'success' => true,
946 'code' => 200,
947 'file_url' => $this->generate_file_url($upload_path),
948 'key' => $upload_path,
949 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync')
950 );
951 } else {
952 $result = array(
953 'success' => false,
954 'code' => 200,
955 'message' => esc_html__('Something happened while uploading to server', 'media-cloud-sync')
956 );
957 }
958 } catch (MultipartUploadException $e) {
959 $result = array(
960 'success' => false,
961 'code' => 200,
962 'message' => $e->getMessage()
963 );
964 }
965 }
966 } else {
967 $result = array(
968 'success' => false,
969 'code' => 200,
970 'message' => esc_html__('Check the file you are trying to upload. Please try again', 'media-cloud-sync')
971 );
972 }
973 } else {
974 $result = array(
975 'success' => false,
976 'code' => 200,
977 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
978 );
979 }
980 return $result;
981 }
982
983 /**
984 * Save object to server
985 * @since 1.0.0
986 */
987 public function object_to_server($key, $save_path) {
988 try {
989 $getObject = $this->s3Client->GetObject([
990 'Bucket' => $this->bucket_name,
991 'Key' => $key,
992 'SaveAs' => $save_path
993 ]);
994 if (file_exists($save_path)) {
995 return true;
996 }
997 } catch (AwsException $e) {
998 return false;
999 }
1000 return false;
1001 }
1002
1003
1004 /**
1005 * Delete Single
1006 * @since 1.0.0
1007 * @return boolean
1008 */
1009 public function deleteSingle($key) {
1010 $result = array();
1011 if (isset($key) && !empty($key)) {
1012 try {
1013 $this->s3Client->deleteObject([
1014 'Bucket' => $this->bucket_name,
1015 'Key' => $key
1016 ]);
1017
1018 if (!$this->s3Client->doesObjectExist($this->bucket_name, $key)) {
1019 $result = array(
1020 'success' => true,
1021 'code' => 200,
1022 'message' => esc_html__('Deleted Successfully', 'media-cloud-sync')
1023 );
1024 } else {
1025 $result = array(
1026 'success' => false,
1027 'code' => 200,
1028 'message' => esc_html__('File not deleted', 'media-cloud-sync')
1029 );
1030 }
1031 } catch (AwsException $e) {
1032 $result = array(
1033 'success' => false,
1034 'code' => 200,
1035 'message' => $e->getMessage()
1036 );
1037 }
1038 } else {
1039 $result = array(
1040 'success' => false,
1041 'code' => 200,
1042 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
1043 );
1044 }
1045 return $result;
1046 }
1047
1048 /**
1049 * get presigned URL
1050 * @since 1.0.0
1051 * @return boolean
1052 */
1053 public function get_presigned_url($key) {
1054 $result = array();
1055 if (isset($key) && !empty($key)) {
1056 try {
1057 $cmd = $this->s3Client->getCommand('GetObject', [
1058 'Bucket' => $this->bucket_name,
1059 'Key' => $key
1060 ]);
1061
1062 $expires = isset($this->settings['presigned_expire']) ? $this->settings['presigned_expire'] : 20;
1063
1064 $request = $this->s3Client->createPresignedRequest($cmd, sprintf('+%s minutes', $expires));
1065
1066 if ($presignedUrl = (string)$request->getUri()) {
1067 $result = array(
1068 'success' => true,
1069 'code' => 200,
1070 'file_url' => $presignedUrl,
1071 'message' => esc_html__('Got Presigned URL Successfully', 'media-cloud-sync')
1072 );
1073 } else {
1074 $result = array(
1075 'success' => false,
1076 'code' => 200,
1077 'message' => esc_html__('Error getting presigned URL', 'media-cloud-sync')
1078 );
1079 }
1080 } catch (AwsException $e) {
1081 $result = array(
1082 'success' => false,
1083 'code' => 200,
1084 'message' => $e->getMessage()
1085 );
1086 }
1087 } else {
1088 $result = array(
1089 'success' => false,
1090 'code' => 200,
1091 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync')
1092 );
1093 }
1094 return $result;
1095 }
1096
1097 /**
1098 * Generate file URL
1099 */
1100 private function generate_file_url($key){
1101 $domain = $this->get_domain();
1102
1103 return apply_filters('wpmcs_generate_s3_file_url',
1104 $domain . '/' . $key,
1105 $domain, $key
1106 );
1107 }
1108
1109 /**
1110 * Get domain URL
1111 */
1112 public function get_domain() {
1113 $region = isset($this->config['region']) ? $this->config['region'] : '';
1114 return "https://{$this->bucket_name}.s3.{$region}.amazonaws.com";
1115 }
1116
1117 }