diff --git a/rustfs/src/admin/handlers/account_info.rs b/rustfs/src/admin/handlers/account_info.rs index d3abbb7b8..cbaa0a414 100644 --- a/rustfs/src/admin/handlers/account_info.rs +++ b/rustfs/src/admin/handlers/account_info.rs @@ -22,6 +22,7 @@ use crate::admin::storage_api::data_usage::{ apply_bucket_usage_memory_overlay, load_data_usage_from_backend, refresh_bucket_usage_from_object_layer, refresh_versioned_bucket_usage_from_object_layer, replace_bucket_usage_memory_from_info, }; +use crate::admin::storage_api::metadata_sys; use crate::auth::get_condition_values; use crate::server::{ADMIN_PREFIX, RemoteAddr}; use http::{HeaderMap, HeaderValue}; @@ -31,6 +32,7 @@ use rustfs_data_usage::BucketUsageInfo; use rustfs_policy::policy::BucketPolicy; use rustfs_policy::policy::default::DEFAULT_POLICIES; use rustfs_policy::policy::{Args, action::Action, action::S3Action}; +use s3s::dto::{ObjectLockConfiguration, ObjectLockEnabled, ReplicationConfiguration, ReplicationRuleStatus}; use s3s::header::CONTENT_TYPE; use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, s3_error}; use serde::Serialize; @@ -74,6 +76,34 @@ fn apply_usage_to_bucket_access_info(bucket_info: &mut rustfs_madmin::BucketAcce bucket_info.object_versions_histogram = usage.object_versions_histogram.clone(); } +fn object_lock_config_enabled(config: &ObjectLockConfiguration) -> bool { + config + .object_lock_enabled + .as_ref() + .is_some_and(|enabled| enabled.as_str() == ObjectLockEnabled::ENABLED) +} + +fn replication_config_enabled(config: &ReplicationConfiguration) -> bool { + config + .rules + .iter() + .any(|rule| rule.status.as_str() == ReplicationRuleStatus::ENABLED) +} + +async fn bucket_locking_enabled(bucket: &str) -> bool { + metadata_sys::get_object_lock_config(bucket) + .await + .ok() + .is_some_and(|(config, _)| object_lock_config_enabled(&config)) +} + +async fn bucket_replication_enabled(bucket: &str) -> bool { + metadata_sys::get_replication_config(bucket) + .await + .ok() + .is_some_and(|(config, _)| replication_config_enabled(&config)) +} + #[async_trait::async_trait] impl Operation for AccountInfoHandler { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { @@ -229,13 +259,13 @@ impl Operation for AccountInfoHandler { let (rd, wr) = is_allow(bucket.name.clone()).await; if rd || wr { // TODO: BucketQuotaSys - // TODO: other attributes let mut bucket_info = rustfs_madmin::BucketAccessInfo { name: bucket.name.clone(), details: Some(rustfs_madmin::BucketDetails { versioning: BucketVersioningSys::enabled(bucket.name.as_str()).await, versioning_suspended: BucketVersioningSys::suspended(bucket.name.as_str()).await, - ..Default::default() + locking: bucket_locking_enabled(bucket.name.as_str()).await, + replication: bucket_replication_enabled(bucket.name.as_str()).await, }), created: bucket.created, access: rustfs_madmin::AccountAccess { read: rd, write: wr }, @@ -270,6 +300,7 @@ mod tests { use super::*; use rustfs_madmin::BackendInfo; use rustfs_policy::policy::BucketPolicy; + use s3s::dto::{Destination, ReplicationRule}; #[test] fn test_account_info_structure() { @@ -339,4 +370,46 @@ mod tests { assert_eq!(bucket_info.size, 5); assert_eq!(bucket_info.objects, 2); } + + #[test] + fn accountinfo_bucket_details_marks_object_lock_enabled() { + let enabled = ObjectLockConfiguration { + object_lock_enabled: Some(ObjectLockEnabled::from_static(ObjectLockEnabled::ENABLED)), + ..Default::default() + }; + let disabled = ObjectLockConfiguration::default(); + + assert!(object_lock_config_enabled(&enabled)); + assert!(!object_lock_config_enabled(&disabled)); + } + + #[test] + fn accountinfo_bucket_details_marks_replication_enabled_rules() { + let enabled = replication_config_with_status(ReplicationRuleStatus::ENABLED); + let disabled = replication_config_with_status(ReplicationRuleStatus::DISABLED); + + assert!(replication_config_enabled(&enabled)); + assert!(!replication_config_enabled(&disabled)); + } + + fn replication_config_with_status(status: &'static str) -> ReplicationConfiguration { + ReplicationConfiguration { + role: "arn:aws:iam::123456789012:role/replication".to_string(), + rules: vec![ReplicationRule { + delete_marker_replication: None, + delete_replication: None, + destination: Destination { + bucket: "arn:aws:s3:::target".to_string(), + ..Default::default() + }, + existing_object_replication: None, + filter: None, + id: None, + prefix: None, + priority: None, + source_selection_criteria: None, + status: ReplicationRuleStatus::from_static(status), + }], + } + } }