From 740795c9a200f8924ee5cb2f451e0f5f76e3bbb3 Mon Sep 17 00:00:00 2001 From: antoine-de Date: Tue, 10 Feb 2026 10:19:09 +0100 Subject: [PATCH] feat(services/s3): Add a way to specify a default ACL This default ACL is used when creating an object. https://github.com/apache/opendal/issues/5358 Note: I did not use a fixed list for the ACL but a String since I'm not an expert on this, and I don't really know if, for all supported services, we can restrict it the list of [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl). --- core/services/s3/src/backend.rs | 7 +++++++ core/services/s3/src/config.rs | 4 ++++ core/services/s3/src/core.rs | 8 ++++++++ core/services/s3/src/docs.md | 31 +++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/core/services/s3/src/backend.rs b/core/services/s3/src/backend.rs index d3ae6aa76787..3c71d75e89af 100644 --- a/core/services/s3/src/backend.rs +++ b/core/services/s3/src/backend.rs @@ -682,6 +682,12 @@ impl S3Builder { None } + + /// Set default ACL for new objects. + pub fn default_acl(mut self, acl: &str) -> Self { + self.config.default_acl = Some(acl.to_string()); + self + } } impl Builder for S3Builder { @@ -960,6 +966,7 @@ impl Builder for S3Builder { enable_request_payer: config.enable_request_payer, signer, checksum_algorithm, + default_acl: config.default_acl, }), }) } diff --git a/core/services/s3/src/config.rs b/core/services/s3/src/config.rs index 8e4a3c15dc21..8c219de6c707 100644 --- a/core/services/s3/src/config.rs +++ b/core/services/s3/src/config.rs @@ -221,6 +221,10 @@ pub struct S3Config { /// Indicates whether the client agrees to pay for the requests made to the S3 bucket. #[serde(alias = "aws_request_payer", alias = "request_payer")] pub enable_request_payer: bool, + + /// Default ACL for new objects. + /// Note that some s3 services like minio do not support this option. + pub default_acl: Option, } impl Debug for S3Config { diff --git a/core/services/s3/src/core.rs b/core/services/s3/src/core.rs index 03e14483a778..bdb1ec470cf1 100644 --- a/core/services/s3/src/core.rs +++ b/core/services/s3/src/core.rs @@ -75,6 +75,8 @@ pub mod constants { pub const X_AMZ_VERSION_ID: &str = "x-amz-version-id"; pub const X_AMZ_OBJECT_SIZE: &str = "x-amz-object-size"; + pub const X_AMZ_ACL: &str = "x-amz-acl"; + pub const RESPONSE_CONTENT_DISPOSITION: &str = "response-content-disposition"; pub const RESPONSE_CONTENT_TYPE: &str = "response-content-type"; pub const RESPONSE_CACHE_CONTROL: &str = "response-cache-control"; @@ -97,6 +99,7 @@ pub struct S3Core { pub allow_anonymous: bool, pub disable_list_objects_v2: bool, pub enable_request_payer: bool, + pub default_acl: Option, pub signer: Signer, pub checksum_algorithm: Option, @@ -330,6 +333,11 @@ impl S3Core { req = req.header(format!("{X_AMZ_META_PREFIX}{key}"), value) } } + + // Set ACL header. + if let Some(acl) = &self.default_acl { + req = req.header(constants::X_AMZ_ACL, acl); + } req } diff --git a/core/services/s3/src/docs.md b/core/services/s3/src/docs.md index 04cc80009474..df279f35b7ec 100644 --- a/core/services/s3/src/docs.md +++ b/core/services/s3/src/docs.md @@ -31,6 +31,7 @@ This service can be used to: - `enable_virtual_host_style`: Enable virtual host style. - `disable_write_with_if_match`: Disable write with if match. - `enable_request_payer`: Enable the request payer for backend. +- `default_acl`: Define the default access control list (ACL) when creating a new object. Note that some s3 services like minio do not support this option. Refer to [`S3Builder`]'s public API docs for more information. @@ -237,3 +238,33 @@ async fn main() -> Result<()> { Ok(()) } ``` + +### S3 with default ACL + +```rust,no_run +use log::info; +use opendal_core::Operator; +use opendal_core::Result; +use opendal_service_s3::S3; + +#[tokio::main] +async fn main() -> Result<()> { + let mut builder = S3::default() + // Setup builders + .root("/path/to/dir") + .bucket("test") + .region("us-east-1") + .endpoint("https://s3.amazonaws.com") + .access_key_id("access_key_id") + .secret_access_key("secret_access_key") + // Enable public-read ACL + .default_acl("public-read"); + + let op = Operator::new(builder)?.finish(); + info!("operator: {:?}", op); + + // New objects will be created with public-read ACL + + Ok(()) +} +```