mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-06 05:17:42 +00:00
fix(auth): restore filtered ListBuckets fallback (#5726)
This commit is contained in:
@@ -222,7 +222,7 @@ test-group = 'ecstore-serial-flaky'
|
||||
[profile.e2e-smoke]
|
||||
default-filter = """
|
||||
package(e2e_test) & (
|
||||
test(/^(delete_marker_migration_semantics|version_id_regression|list_objects_v2_pagination|list_object_versions_regression|list_objects_duplicates|list_buckets_double_slash|leading_slash_key|special_chars|create_bucket_region|delete_objects_versioning|head_object_consistency|head_object_range|copy_object_metadata|copy_object_tagging|copy_source_invalid_date|content_encoding|multipart_storage_class|storage_class_capability|ssec_copy|anonymous_access|bucket_policy_check|presigned_negative|negative_sigv4|admin_auth|notification_webhook|tls_hot_reload|console_smoke|admin_iam_crud|admin_pools|sts_query_compat)_test::|^fake_s3_target::/)
|
||||
test(/^(delete_marker_migration_semantics|version_id_regression|list_objects_v2_pagination|list_object_versions_regression|list_objects_duplicates|list_buckets_double_slash|list_buckets_auth|leading_slash_key|special_chars|create_bucket_region|delete_objects_versioning|head_object_consistency|head_object_range|copy_object_metadata|copy_object_tagging|copy_source_invalid_date|content_encoding|multipart_storage_class|storage_class_capability|ssec_copy|anonymous_access|bucket_policy_check|presigned_negative|negative_sigv4|admin_auth|notification_webhook|tls_hot_reload|console_smoke|admin_iam_crud|admin_pools|sts_query_compat)_test::|^fake_s3_target::/)
|
||||
| test(/^replication_extension_test::(test_replication_check_succeeds_with_remote_target|test_replication_check_rejects_target_without_object_lock|test_set_remote_target_rejects_unversioned_source_bucket|test_replication_check_rejects_unversioned_source_bucket|test_replication_check_rejects_missing_replication_config|test_replication_check_rejects_invalid_bucket|test_set_remote_target_rejects_same_bucket_on_same_deployment|test_set_remote_target_rejects_unversioned_target_bucket|test_set_remote_target_update_requires_arn|test_set_remote_target_update_rejects_missing_target|test_set_remote_target_rejects_invalid_target_url|test_set_remote_target_rejects_self_signed_https_target_without_skip_tls_verify|test_set_remote_target_rejects_private_ca_https_target_without_ca_cert_pem|test_list_remote_targets_rejects_empty_bucket|test_list_remote_targets_rejects_invalid_bucket|test_remove_remote_target_rejects_missing_target|test_remove_remote_target_rejects_missing_arn|test_remove_remote_target_rejects_invalid_bucket|test_remove_remote_target_rejects_target_used_by_replication|test_delete_bucket_replication_removes_remote_target)$/)
|
||||
| test(/^reliant::lifecycle::/)
|
||||
| test(/^reliant::tiering::/)
|
||||
|
||||
@@ -564,7 +564,12 @@ impl RustFSTestEnvironment {
|
||||
|
||||
/// Create an AWS S3 client configured for this RustFS instance
|
||||
pub fn create_s3_client(&self) -> Client {
|
||||
Client::from_conf(build_test_s3_config(&self.url, &self.access_key, &self.secret_key, "e2e-test"))
|
||||
self.create_s3_client_with_credentials(&self.access_key, &self.secret_key)
|
||||
}
|
||||
|
||||
/// Create an AWS S3 client with explicit credentials for this RustFS instance.
|
||||
pub fn create_s3_client_with_credentials(&self, access_key: &str, secret_key: &str) -> Client {
|
||||
Client::from_conf(build_test_s3_config(&self.url, access_key, secret_key, "e2e-test"))
|
||||
}
|
||||
|
||||
/// Create test bucket
|
||||
|
||||
@@ -290,6 +290,10 @@ mod overwrite_cleanup_regression_test;
|
||||
#[cfg(test)]
|
||||
mod list_buckets_double_slash_test;
|
||||
|
||||
// Regression coverage for bucket-scoped ListBuckets authorization fallback.
|
||||
#[cfg(test)]
|
||||
mod list_buckets_auth_test;
|
||||
|
||||
// Regression test for backlog#629(b): region-aware CreateBucket SigV4.
|
||||
#[cfg(test)]
|
||||
mod create_bucket_region_test;
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//! Regression coverage for the MinIO-compatible filtered ListBuckets fallback.
|
||||
|
||||
use crate::common::{RustFSTestEnvironment, admin_ok, init_logging};
|
||||
use std::error::Error;
|
||||
|
||||
type TestResult = Result<(), Box<dyn Error + Send + Sync>>;
|
||||
|
||||
#[tokio::test]
|
||||
async fn bucket_scoped_policy_returns_only_authorized_bucket() -> TestResult {
|
||||
init_logging();
|
||||
|
||||
let mut env = RustFSTestEnvironment::new().await?;
|
||||
env.start_rustfs_server(vec![]).await?;
|
||||
|
||||
let root_client = env.create_s3_client();
|
||||
let allowed_bucket = "list-buckets-authorized";
|
||||
let hidden_bucket = "list-buckets-hidden";
|
||||
let user = "listbucketsuser";
|
||||
let secret = "listbucketssecret";
|
||||
let policy = "list-buckets-scoped";
|
||||
|
||||
root_client.create_bucket().bucket(allowed_bucket).send().await?;
|
||||
root_client.create_bucket().bucket(hidden_bucket).send().await?;
|
||||
|
||||
admin_ok(
|
||||
&env,
|
||||
http::Method::PUT,
|
||||
&format!("/rustfs/admin/v3/add-canned-policy?name={policy}"),
|
||||
Some(
|
||||
serde_json::json!({
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:*"],
|
||||
"Resource": [
|
||||
format!("arn:aws:s3:::{allowed_bucket}"),
|
||||
format!("arn:aws:s3:::{allowed_bucket}/*")
|
||||
]
|
||||
}]
|
||||
})
|
||||
.to_string(),
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
admin_ok(
|
||||
&env,
|
||||
http::Method::PUT,
|
||||
&format!("/rustfs/admin/v3/add-user?accessKey={user}"),
|
||||
Some(serde_json::json!({ "secretKey": secret, "status": "enabled" }).to_string()),
|
||||
)
|
||||
.await?;
|
||||
admin_ok(
|
||||
&env,
|
||||
http::Method::POST,
|
||||
"/rustfs/admin/v3/idp/builtin/policy/attach",
|
||||
Some(serde_json::json!({ "policies": [policy], "user": user }).to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let client = env.create_s3_client_with_credentials(user, secret);
|
||||
// Capture ListBuckets first so the direct-access control cannot warm bucket metadata and mask the regression.
|
||||
let listed = client.list_buckets().send().await;
|
||||
client.list_objects_v2().bucket(allowed_bucket).send().await?;
|
||||
|
||||
let listed = listed?;
|
||||
let names = listed
|
||||
.buckets()
|
||||
.iter()
|
||||
.filter_map(|bucket| bucket.name().map(ToOwned::to_owned))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(names, vec![allowed_bucket]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -858,6 +858,10 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if action == Action::S3Action(S3Action::ListAllMyBucketsAction) {
|
||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||
}
|
||||
|
||||
let policy_allowed_fallback = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||
bucket: bucket.as_str(),
|
||||
action,
|
||||
|
||||
Reference in New Issue
Block a user