PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
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
← All changes | includes/base/services/docean.php +70 -32 1.3.121.4.1 View file →
@@ -311,10 +311,17 @@
311 311
312 312
313 313 /**
314 314 * Add Bucket Policy
315 + *
316 + * $private_prefix, when non-empty, carves that path out of the public
317 + * grant entirely — every action in the list, not just reads, so an
318 + * anonymous caller can't read, write, or delete anything under it. Same
319 + * NotResource approach as S3::putBucketPolicy() — Spaces' policy API is
320 + * S3-compatible, so the identical fix applies unchanged.
321 + * @since 1.0.0
315 322 */
316 - private function putBucketPolicy($bucket, $DOClient = false) {
323 + private function putBucketPolicy($bucket, $DOClient = false, $private_prefix = '') {
317 324 if($DOClient == false) {
318 325 $DOClient = $this->DOClient;
319 326 }
320 327
@@ -319,38 +326,45 @@
319 326 }
320 327
321 328 if(empty($bucket)) return false;
322 329
330 + $actions = [
331 + "s3:DeleteObjectTagging",
332 + "s3:ListBucketMultipartUploads",
333 + "s3:DeleteObjectVersion",
334 + "s3:ListBucket",
335 + "s3:DeleteObjectVersionTagging",
336 + "s3:GetBucketAcl",
337 + "s3:ListMultipartUploadParts",
338 + "s3:PutObject",
339 + "s3:GetObjectAcl",
340 + "s3:GetObject",
341 + "s3:AbortMultipartUpload",
342 + "s3:DeleteObject",
343 + "s3:GetBucketLocation",
344 + "s3:PutObjectAcl",
345 + "s3:putBucketOwnershipControls",
346 + "s3:putBucketPolicy"
347 + ];
348 +
349 + $statement = [
350 + "Effect" => "Allow",
351 + "Principal" => "*",
352 + "Action" => $actions,
353 + ];
354 +
355 + if (!empty($private_prefix)) {
356 + $statement["NotResource"] = ["arn:aws:s3:::$bucket/$private_prefix/*"];
357 + } else {
358 + $statement["Resource"] = [
359 + "arn:aws:s3:::$bucket/*",
360 + "arn:aws:s3:::$bucket"
361 + ];
362 + }
363 +
323 364 $policy = json_encode([
324 - "Version" => "2012-10-17",
325 - "Statement" => [
326 - [
327 - "Effect" => "Allow",
328 - "Principal" => "*",
329 - "Action" => [
330 - "s3:DeleteObjectTagging",
331 - "s3:ListBucketMultipartUploads",
332 - "s3:DeleteObjectVersion",
333 - "s3:ListBucket",
334 - "s3:DeleteObjectVersionTagging",
335 - "s3:GetBucketAcl",
336 - "s3:ListMultipartUploadParts",
337 - "s3:PutObject",
338 - "s3:GetObjectAcl",
339 - "s3:GetObject",
340 - "s3:AbortMultipartUpload",
341 - "s3:DeleteObject",
342 - "s3:GetBucketLocation",
343 - "s3:PutObjectAcl",
344 - "s3:putBucketOwnershipControls",
345 - "s3:putBucketPolicy"
346 - ],
347 - "Resource" => [
348 - "arn:aws:s3:::$bucket/*",
349 - "arn:aws:s3:::$bucket"
350 - ]
351 - ]
352 - ]
365 + "Version" => "2012-10-17",
366 + "Statement" => [$statement]
353 367 ]);
354 368
355 369 try {
356 370 // Add bucket policy
@@ -365,10 +379,27 @@
365 379 return false; // Handle general exceptions
366 380 }
367 381 }
368 382
383 + /**
384 + * Apply (or, with an empty $private_prefix, un-apply) the private-path
385 + * bucket policy carve-out.
386 + * @since 1.0.0
387 + */
388 + public function applyPrivatePathPolicy($private_prefix) {
389 + if (!$this->DOClient || empty($this->bucket_name)) {
390 + return ['success' => false, 'code' => 200, 'message' => esc_html__('Client not configured', 'media-cloud-sync')];
391 + }
369 392
393 + $ok = $this->putBucketPolicy($this->bucket_name, $this->DOClient, $private_prefix);
370 394
395 + return $ok
396 + ? ['success' => true, 'code' => 200, 'message' => esc_html__('Policy applied successfully', 'media-cloud-sync')]
397 + : ['success' => false, 'code' => 200, 'message' => esc_html__('Failed to apply bucket policy', 'media-cloud-sync')];
398 + }
399 +
400 +
401 +
371 402 /**
372 403 * Check Bucket Write Permission
373 404 * @since 1.0.0
374 405 */
@@ -724,9 +755,9 @@
724 755 * Upload Single
725 756 * @since 1.0.0
726 757 * @return boolean
727 758 */
728 - public function uploadSingle($absolute_source_path, $relative_source_path, $prefix='') {
759 + public function uploadSingle($absolute_source_path, $relative_source_path, $prefix='', $is_private = false) {
729 760 if (
730 761 isset($absolute_source_path) && !empty($absolute_source_path) &&
731 762 isset($relative_source_path) && !empty($relative_source_path)
732 763 ) {
@@ -731,9 +762,16 @@
731 762 isset($relative_source_path) && !empty($relative_source_path)
732 763 ) {
733 764 $file_name = wp_basename( $relative_source_path );
734 765 if ($file_name) {
735 - $upload_path = Utils::generate_object_key($relative_source_path, $prefix);
766 + $upload_path = Utils::generate_object_key($relative_source_path, $prefix, $is_private);
767 + if ($upload_path === false) {
768 + return [
769 + 'success' => false,
770 + 'code' => 200,
771 + 'message' => esc_html__('This file is marked private, but the private-media add-on is not currently active — reupload skipped to avoid exposing it.', 'media-cloud-sync')
772 + ];
773 + }
736 774 return $this->execute_upload($absolute_source_path, $upload_path);
737 775 }
738 776 return [
739 777 'success' => false,