refactor(storage): extract ACL response builders into s3_api (#1880)

This commit is contained in:
安正超
2026-02-19 22:57:35 +08:00
committed by GitHub
parent a4e8e1fd5e
commit 583377d2a5
4 changed files with 99 additions and 49 deletions

View File

@@ -23,11 +23,11 @@ use crate::storage::head_prefix::{head_prefix_not_found_message, probe_prefix_ha
use crate::storage::helper::OperationHelper;
use crate::storage::options::{filter_object_metadata, get_content_sha256};
use crate::storage::readers::InMemoryAsyncReader;
use crate::storage::s3_api::acl::{build_get_bucket_acl_output, build_get_object_acl_output};
use crate::storage::s3_api::bucket::{
build_list_buckets_output, build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output,
parse_list_object_versions_params, parse_list_objects_v2_params,
};
use crate::storage::s3_api::common::rustfs_owner;
use crate::storage::s3_api::multipart::{
build_list_multipart_uploads_output, build_list_parts_output, parse_list_multipart_uploads_params, parse_list_parts_params,
};
@@ -148,13 +148,7 @@ use s3s::header::{X_AMZ_RESTORE, X_AMZ_RESTORE_OUTPUT_PATH};
use s3s::{S3, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, dto::*, s3_error};
use std::convert::Infallible;
use std::ops::Add;
use std::{
collections::HashMap,
fmt::Debug,
path::Path,
str::FromStr,
sync::{Arc, LazyLock},
};
use std::{collections::HashMap, fmt::Debug, path::Path, str::FromStr, sync::Arc};
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
@@ -174,9 +168,6 @@ macro_rules! try_ {
};
}
// Shared owner metadata source for S3 response compatibility.
pub(crate) static RUSTFS_OWNER: LazyLock<Owner> = LazyLock::new(rustfs_owner);
#[derive(Debug, Clone)]
pub struct FS {
// pub store: ECStore,
@@ -1957,21 +1948,7 @@ impl S3 for FS {
.await
.map_err(ApiError::from)?;
let grants = vec![Grant {
grantee: Some(Grantee {
type_: Type::from_static(Type::CANONICAL_USER),
display_name: None,
email_address: None,
id: None,
uri: None,
}),
permission: Some(Permission::from_static(Permission::FULL_CONTROL)),
}];
Ok(s3_response(GetBucketAclOutput {
grants: Some(grants),
owner: Some(RUSTFS_OWNER.to_owned()),
}))
Ok(s3_response(build_get_bucket_acl_output()))
}
#[instrument(level = "debug", skip(self))]
@@ -2900,22 +2877,7 @@ impl S3 for FS {
return Err(S3Error::with_message(S3ErrorCode::InternalError, format!("{e}")));
}
let grants = vec![Grant {
grantee: Some(Grantee {
type_: Type::from_static(Type::CANONICAL_USER),
display_name: None,
email_address: None,
id: None,
uri: None,
}),
permission: Some(Permission::from_static(Permission::FULL_CONTROL)),
}];
Ok(s3_response(GetObjectAclOutput {
grants: Some(grants),
owner: Some(RUSTFS_OWNER.to_owned()),
..Default::default()
}))
Ok(s3_response(build_get_object_acl_output()))
}
async fn get_object_attributes(

View File

@@ -16,7 +16,7 @@
mod tests {
use crate::config::workload_profiles::WorkloadProfile;
use crate::storage::ecfs::FS;
use crate::storage::ecfs::RUSTFS_OWNER;
use crate::storage::s3_api::common::rustfs_owner;
use crate::storage::{
apply_cors_headers, check_preconditions, get_adaptive_buffer_size_with_profile, get_buffer_size_opt_in, is_etag_equal,
matches_origin_pattern, parse_etag, parse_object_lock_legal_hold, parse_object_lock_retention,
@@ -67,11 +67,12 @@ mod tests {
}
#[test]
fn test_rustfs_owner_constant() {
// Test that RUSTFS_OWNER constant is properly defined
assert!(!RUSTFS_OWNER.display_name.as_ref().unwrap().is_empty());
assert!(!RUSTFS_OWNER.id.as_ref().unwrap().is_empty());
assert_eq!(RUSTFS_OWNER.display_name.as_ref().unwrap(), "rustfs");
fn test_rustfs_owner_helper() {
// Test that rustfs owner metadata remains stable for S3 compatibility.
let owner = rustfs_owner();
assert!(!owner.display_name.as_ref().unwrap().is_empty());
assert!(!owner.id.as_ref().unwrap().is_empty());
assert_eq!(owner.display_name.as_ref().unwrap(), "rustfs");
}
// Note: Most S3 API methods require complex setup with global state, storage backend,

View File

@@ -0,0 +1,87 @@
// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::storage::s3_api::common::rustfs_owner;
use s3s::dto::{GetBucketAclOutput, GetObjectAclOutput, Grant, Grantee, Permission, Type};
fn full_control_grants() -> Vec<Grant> {
vec![Grant {
grantee: Some(Grantee {
type_: Type::from_static(Type::CANONICAL_USER),
display_name: None,
email_address: None,
id: None,
uri: None,
}),
permission: Some(Permission::from_static(Permission::FULL_CONTROL)),
}]
}
pub(crate) fn build_get_bucket_acl_output() -> GetBucketAclOutput {
GetBucketAclOutput {
grants: Some(full_control_grants()),
owner: Some(rustfs_owner()),
}
}
pub(crate) fn build_get_object_acl_output() -> GetObjectAclOutput {
GetObjectAclOutput {
grants: Some(full_control_grants()),
owner: Some(rustfs_owner()),
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::{build_get_bucket_acl_output, build_get_object_acl_output};
use crate::storage::s3_api::common::rustfs_owner;
use s3s::dto::{Permission, Type};
#[test]
fn test_build_get_bucket_acl_output_contains_full_control_grant_and_owner() {
let output = build_get_bucket_acl_output();
let grants = output.grants.as_ref().expect("grants should be present");
let owner = output.owner.as_ref().expect("owner should be present");
assert_eq!(grants.len(), 1);
assert_eq!(grants[0].permission.as_ref().map(Permission::as_str), Some(Permission::FULL_CONTROL));
assert_eq!(
grants[0].grantee.as_ref().map(|grantee| grantee.type_.as_str()),
Some(Type::CANONICAL_USER)
);
let expected_owner = rustfs_owner();
assert_eq!(owner.display_name, expected_owner.display_name);
assert_eq!(owner.id, expected_owner.id);
}
#[test]
fn test_build_get_object_acl_output_contains_full_control_grant_and_owner() {
let output = build_get_object_acl_output();
let grants = output.grants.as_ref().expect("grants should be present");
let owner = output.owner.as_ref().expect("owner should be present");
assert_eq!(grants.len(), 1);
assert_eq!(grants[0].permission.as_ref().map(Permission::as_str), Some(Permission::FULL_CONTROL));
assert_eq!(
grants[0].grantee.as_ref().map(|grantee| grantee.type_.as_str()),
Some(Type::CANONICAL_USER)
);
let expected_owner = rustfs_owner();
assert_eq!(owner.display_name, expected_owner.display_name);
assert_eq!(owner.id, expected_owner.id);
}
}

View File

@@ -17,7 +17,7 @@
//! This file intentionally starts as skeleton-only. Behavior remains in place
//! until each helper is moved with dedicated small refactor steps.
pub(crate) mod acl {}
pub(crate) mod acl;
pub(crate) mod bucket;
pub(crate) mod common;
pub(crate) mod encryption {}