fix(authz): preserve direct delete usecase callers (#5614)

* fix(authz): preserve direct delete usecase callers

* test(ilm): provide owner context for forced deletes
This commit is contained in:
cxymds
2026-08-02 17:01:32 +08:00
committed by GitHub
parent 93b38b3fbd
commit 7463655ae1
2 changed files with 34 additions and 2 deletions
@@ -1495,6 +1495,8 @@ async fn cancelled_transition_waiting_for_prepared_reader_cleans_remote() {
#[serial]
#[ignore = "global-state ILM integration test: runs serialized in the CI ILM Integration (serial) lane, see ci.yml test-ilm-integration-serial and rustfs/backlog#1148 (ilm-1)"]
async fn delete_transitioned_object_removes_remote_tier_copy_via_usecase() {
use super::storage_api::test::ReqInfo;
let (_disk_paths, ecstore) = setup_test_env().await;
let usecase = DefaultObjectUsecase::from_global();
@@ -1531,6 +1533,11 @@ async fn delete_transitioned_object_removes_remote_tier_copy_via_usecase() {
Method::DELETE,
);
insert_header(&mut req.headers, SUFFIX_FORCE_DELETE, "true");
req.extensions.insert(ReqInfo {
cred: Some(rustfs_credentials::Credentials::default()),
is_owner: true,
..Default::default()
});
Box::pin(usecase.execute_delete_object(req))
.await
@@ -1735,6 +1742,8 @@ async fn compensation_driven_complete_multipart_upload_still_transitions() {
#[serial]
#[ignore = "global-state ILM integration test: runs serialized in the CI ILM Integration (serial) lane, see ci.yml test-ilm-integration-serial and rustfs/backlog#1148 (ilm-1)"]
async fn compensation_driven_transition_still_cleans_remote_tier_on_delete() {
use super::storage_api::test::ReqInfo;
let (_disk_paths, ecstore) = setup_test_env().await;
let usecase = DefaultObjectUsecase::from_global();
@@ -1771,6 +1780,11 @@ async fn compensation_driven_transition_still_cleans_remote_tier_on_delete() {
Method::DELETE,
);
insert_header(&mut req.headers, SUFFIX_FORCE_DELETE, "true");
req.extensions.insert(ReqInfo {
cred: Some(rustfs_credentials::Credentials::default()),
is_owner: true,
..Default::default()
});
Box::pin(usecase.execute_delete_object(req))
.await
+20 -2
View File
@@ -6615,7 +6615,8 @@ impl DefaultObjectUsecase {
));
}
if !recursive_force_delete_is_authorized(&req.headers, req_info_ref(&req)?.is_owner, false) {
let is_owner = req_info_ref(&req).map(|info| info.is_owner).unwrap_or(false);
if !recursive_force_delete_is_authorized(&req.headers, is_owner, false) {
return Err(S3Error::with_message(
S3ErrorCode::AccessDenied,
"Recursive force-delete is restricted to administrative requests",
@@ -7081,7 +7082,7 @@ impl DefaultObjectUsecase {
authorize_request(&mut req, Action::S3Action(S3Action::ReplicateDeleteAction)).await?;
}
let is_owner = req_info_ref(&req)?.is_owner;
let is_owner = req_info_ref(&req).map(|info| info.is_owner).unwrap_or(false);
if !recursive_force_delete_is_authorized(&req.headers, is_owner, replica) {
return Err(S3Error::with_message(
S3ErrorCode::AccessDenied,
@@ -13386,6 +13387,23 @@ mod tests {
let err = usecase.execute_delete_objects(req).await.unwrap_err();
assert_eq!(err.code(), &S3ErrorCode::InternalError);
assert_eq!(err.message(), Some("Not init"));
}
#[tokio::test]
async fn execute_delete_object_allows_non_force_request_without_req_info_until_store_lookup() {
let input = DeleteObjectInput::builder()
.bucket("test-bucket".to_string())
.key("test-key".to_string())
.build()
.unwrap();
let err = DefaultObjectUsecase::without_context()
.execute_delete_object(build_request(input, Method::DELETE))
.await
.expect_err("an uninitialized store should be reported after non-force admission");
assert_eq!(err.code(), &S3ErrorCode::InternalError);
assert_eq!(err.message(), Some("Not init"));
}
#[test]