mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-28 09:08:58 +00:00
fix: honor bucket-scoped ListBucket policies with s3:prefix (#2707)
Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
@@ -35,7 +35,7 @@ use s3s::access::{S3Access, S3AccessContext};
|
||||
use s3s::{S3Error, S3ErrorCode, S3Request, S3Result, dto::*, s3_error};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
use url::Url;
|
||||
use url::{Url, form_urlencoded};
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub(crate) struct ReqInfo {
|
||||
@@ -220,6 +220,30 @@ fn action_tag_metric_label(action: &Action) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_list_bucket_query_conditions(action: Action, query: Option<&str>, conditions: &mut HashMap<String, Vec<String>>) {
|
||||
if !matches!(
|
||||
action,
|
||||
Action::S3Action(
|
||||
S3Action::ListBucketAction | S3Action::ListBucketVersionsAction | S3Action::ListBucketMultipartUploadsAction
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(query) = query else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (key, value) in form_urlencoded::parse(query.as_bytes()) {
|
||||
match key.as_ref() {
|
||||
"prefix" | "delimiter" | "max-keys" => {
|
||||
conditions.entry(key.into_owned()).or_default().push(value.into_owned());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_fs() -> &'static FS {
|
||||
static AUTH_FS: OnceLock<FS> = OnceLock::new();
|
||||
AUTH_FS.get_or_init(FS::new)
|
||||
@@ -312,6 +336,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
let claims = cred.claims.as_ref().unwrap_or(&default_claims);
|
||||
let mut conditions =
|
||||
get_condition_values_with_query(&req.headers, cred, version_id.as_deref(), None, remote_addr, req.uri.query());
|
||||
merge_list_bucket_query_conditions(action, req.uri.query(), &mut conditions);
|
||||
|
||||
let action_args = Args {
|
||||
account: &cred.access_key,
|
||||
@@ -526,6 +551,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
remote_addr,
|
||||
req.uri.query(),
|
||||
);
|
||||
merge_list_bucket_query_conditions(action, req.uri.query(), &mut conditions);
|
||||
|
||||
let no_groups: Option<Vec<String>> = None;
|
||||
let bucket_tag_hint = if !bucket.is_empty() && !object.is_empty() {
|
||||
@@ -1999,6 +2025,46 @@ mod tests {
|
||||
|
||||
/// Object tag conditions must use keys like ExistingObjectTag/<tag-key> so that
|
||||
/// bucket policy conditions (e.g. s3:ExistingObjectTag/security) are evaluated correctly.
|
||||
#[test]
|
||||
fn test_merge_list_bucket_query_conditions_extracts_supported_keys() {
|
||||
let mut conditions = HashMap::new();
|
||||
merge_list_bucket_query_conditions(
|
||||
Action::S3Action(S3Action::ListBucketAction),
|
||||
Some("prefix=photos%2F2024%2F&delimiter=%2F&max-keys=10&encoding-type=url"),
|
||||
&mut conditions,
|
||||
);
|
||||
|
||||
assert_eq!(conditions.get("prefix"), Some(&vec!["photos/2024/".to_string()]));
|
||||
assert_eq!(conditions.get("delimiter"), Some(&vec!["/".to_string()]));
|
||||
assert_eq!(conditions.get("max-keys"), Some(&vec!["10".to_string()]));
|
||||
assert!(!conditions.contains_key("encoding-type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_list_bucket_query_conditions_preserves_empty_prefix_signal() {
|
||||
let mut conditions = HashMap::new();
|
||||
merge_list_bucket_query_conditions(
|
||||
Action::S3Action(S3Action::ListBucketVersionsAction),
|
||||
Some("prefix=&delimiter=%2F"),
|
||||
&mut conditions,
|
||||
);
|
||||
|
||||
assert_eq!(conditions.get("prefix"), Some(&vec![String::new()]));
|
||||
assert_eq!(conditions.get("delimiter"), Some(&vec!["/".to_string()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_list_bucket_query_conditions_ignores_non_list_actions() {
|
||||
let mut conditions = HashMap::new();
|
||||
merge_list_bucket_query_conditions(
|
||||
Action::S3Action(S3Action::GetObjectAction),
|
||||
Some("prefix=photos%2F2024%2F&delimiter=%2F&max-keys=10"),
|
||||
&mut conditions,
|
||||
);
|
||||
|
||||
assert!(conditions.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_object_tag_conditions_key_format() {
|
||||
let mut tags = HashMap::new();
|
||||
|
||||
Reference in New Issue
Block a user