mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
fix(admin): decode compat payload in set-remote-target (#2216)
Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: heihutu <heihutu@gmail.com> Co-authored-by: 马登山 <Cxymds@qq.com> Co-authored-by: 安正超 <anzhengchao@gmail.com> Co-authored-by: loverustfs <hello@rustfs.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
use crate::admin::auth::validate_admin_request;
|
||||
use crate::admin::router::{AdminOperation, Operation, S3Router};
|
||||
use crate::admin::utils::read_compatible_admin_body;
|
||||
use crate::auth::{check_key_valid, get_session_token};
|
||||
use crate::error::ApiError;
|
||||
use crate::server::{ADMIN_PREFIX, RemoteAddr};
|
||||
@@ -21,6 +22,7 @@ use http::{HeaderMap, HeaderValue, Uri};
|
||||
use hyper::{Method, StatusCode};
|
||||
use matchit::Params;
|
||||
use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_ecstore::bucket::bucket_target_sys::BucketTargetSys;
|
||||
use rustfs_ecstore::bucket::metadata::BUCKET_TARGETS_FILE;
|
||||
use rustfs_ecstore::bucket::metadata_sys;
|
||||
@@ -79,7 +81,7 @@ pub fn register_replication_route(r: &mut S3Router<AdminOperation>) -> std::io::
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn validate_replication_admin_request(req: &S3Request<Body>, action: AdminAction) -> S3Result<()> {
|
||||
async fn validate_replication_admin_request(req: &S3Request<Body>, action: AdminAction) -> S3Result<Credentials> {
|
||||
let Some(input_cred) = req.credentials.as_ref() else {
|
||||
return Err(s3_error!(InvalidRequest, "get cred failed"));
|
||||
};
|
||||
@@ -88,7 +90,9 @@ async fn validate_replication_admin_request(req: &S3Request<Body>, action: Admin
|
||||
check_key_valid(get_session_token(&req.uri, &req.headers).unwrap_or_default(), &input_cred.access_key).await?;
|
||||
|
||||
let remote_addr = req.extensions.get::<Option<RemoteAddr>>().and_then(|opt| opt.map(|a| a.0));
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(action)], remote_addr).await
|
||||
validate_admin_request(&req.headers, &cred, owner, false, vec![Action::AdminAction(action)], remote_addr).await?;
|
||||
|
||||
Ok(cred)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -156,7 +160,7 @@ pub struct SetRemoteTargetHandler {}
|
||||
#[async_trait::async_trait]
|
||||
impl Operation for SetRemoteTargetHandler {
|
||||
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
validate_replication_admin_request(&req, AdminAction::SetBucketTargetAction).await?;
|
||||
let cred = validate_replication_admin_request(&req, AdminAction::SetBucketTargetAction).await?;
|
||||
|
||||
let queries = extract_query_params(&req.uri);
|
||||
|
||||
@@ -181,14 +185,14 @@ impl Operation for SetRemoteTargetHandler {
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
let mut input = req.input;
|
||||
let body = match input.store_all_limited(MAX_ADMIN_REQUEST_BODY_SIZE).await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
warn!("get body failed, e: {:?}", e);
|
||||
return Err(s3_error!(InvalidRequest, "remote target configuration body too large or failed to read"));
|
||||
}
|
||||
};
|
||||
let body =
|
||||
match read_compatible_admin_body(req.input, MAX_ADMIN_REQUEST_BODY_SIZE, req.uri.path(), &cred.secret_key).await {
|
||||
Ok(body) => body,
|
||||
Err(e) => {
|
||||
warn!("get body failed, e: {:?}", e);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
let mut remote_target: BucketTarget = serde_json::from_slice(&body).map_err(|e| {
|
||||
error!("Failed to parse BucketTarget from body: {}", e);
|
||||
@@ -221,6 +225,8 @@ impl Operation for SetRemoteTargetHandler {
|
||||
let arn_str = serde_json::to_string(&arn).unwrap_or_default();
|
||||
|
||||
warn!("return exists, arn: {}", arn_str);
|
||||
// MinIO-compatible clients encrypt the request payload for this endpoint,
|
||||
// but they parse the success response directly as plain JSON string ARN.
|
||||
return Ok(S3Response::new((StatusCode::OK, Body::from(arn_str))));
|
||||
}
|
||||
}
|
||||
@@ -276,6 +282,8 @@ impl Operation for SetRemoteTargetHandler {
|
||||
|
||||
let arn_str = serde_json::to_string(&arn).unwrap_or_default();
|
||||
|
||||
// MinIO-compatible clients encrypt the request payload for this endpoint,
|
||||
// but they parse the success response directly as plain JSON string ARN.
|
||||
Ok(S3Response::new((StatusCode::OK, Body::from(arn_str))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,3 +175,39 @@ fn test_phase5_admin_info_contract() {
|
||||
"admin server info path must be served through DefaultAdminUsecase::execute_query_server_info"
|
||||
);
|
||||
}
|
||||
|
||||
fn extract_block_between_markers<'a>(src: &'a str, start_marker: &str, end_marker: &str) -> &'a str {
|
||||
let start = src
|
||||
.find(start_marker)
|
||||
.unwrap_or_else(|| panic!("Expected marker `{}` in source", start_marker));
|
||||
let after_start = &src[start..];
|
||||
let end = after_start
|
||||
.find(end_marker)
|
||||
.unwrap_or_else(|| panic!("Expected end marker `{}` in source", end_marker));
|
||||
&after_start[..end]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replication_set_remote_target_compat_contract() {
|
||||
let replication_src = include_str!("handlers/replication.rs");
|
||||
let handler_block = extract_block_between_markers(
|
||||
replication_src,
|
||||
"impl Operation for SetRemoteTargetHandler",
|
||||
"pub struct ListRemoteTargetHandler",
|
||||
);
|
||||
|
||||
assert!(
|
||||
handler_block.contains("read_compatible_admin_body("),
|
||||
"set-remote-target must decode MinIO-compatible encrypted admin payloads"
|
||||
);
|
||||
|
||||
assert!(
|
||||
handler_block.contains("Body::from(arn_str)"),
|
||||
"set-remote-target must keep ARN success responses as plain JSON string body"
|
||||
);
|
||||
|
||||
assert!(
|
||||
!handler_block.contains("encode_compatible_admin_payload("),
|
||||
"set-remote-target should not re-encrypt ARN success responses"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user