PluginProbe
Media Cloud Sync / 1.2.5
Media Cloud Sync v1.2.5
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 1.2.9 All 35 releases
media-cloud-sync / includes / base / services / s3.php

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

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