fix(auth): authorize DeleteObjects per key (#2814)

This commit is contained in:
GatewayJ
2026-05-06 08:47:36 +08:00
committed by GitHub
parent c228fabfdb
commit 090d60e00a
3 changed files with 169 additions and 13 deletions
+13
View File
@@ -3003,6 +3003,19 @@ impl DefaultObjectUsecase {
continue;
}
if bypass_governance {
let auth_res = authorize_request(&mut req, Action::S3Action(S3Action::BypassGovernanceRetentionAction)).await;
if let Err(e) = auth_res {
delete_results[idx].error = Some(Error {
code: Some("AccessDenied".to_string()),
key: Some(obj_id.key.clone()),
message: Some(e.to_string()),
version_id: version_id.clone(),
});
continue;
}
}
let mut object = ObjectToDelete {
object_name: obj_id.key.clone(),
version_id: version_uuid,
+32 -7
View File
@@ -1069,13 +1069,6 @@ impl S3Access for FS {
req_info.object = None;
req_info.version_id = None;
authorize_request(req, Action::S3Action(S3Action::DeleteObjectAction)).await?;
// S3 Standard: When bypass_governance header is set, must have s3:BypassGovernanceRetention permission
if has_bypass_governance_header(&req.headers) {
authorize_request(req, Action::S3Action(S3Action::BypassGovernanceRetentionAction)).await?;
}
Ok(())
}
@@ -2401,6 +2394,38 @@ mod tests {
assert_eq!(req_info.version_id, None);
}
#[tokio::test]
async fn delete_objects_defers_object_authorization_to_usecase() {
let input = DeleteObjectsInput::builder()
.bucket("test-bucket".to_string())
.delete(Delete {
objects: vec![ObjectIdentifier {
key: "prefix/test-key".to_string(),
version_id: None,
..Default::default()
}],
quiet: None,
})
.build()
.expect("delete objects input should build");
let mut req = build_request(input, Method::POST);
req.extensions.insert(ReqInfo {
cred: Some(rustfs_credentials::Credentials::default()),
..ReqInfo::default()
});
FS::new()
.delete_objects(&mut req)
.await
.expect("DeleteObjects access hook should not require bucket-level DeleteObject");
let req_info = req.extensions.get::<ReqInfo>().expect("req info should remain available");
assert_eq!(req_info.bucket.as_deref(), Some("test-bucket"));
assert_eq!(req_info.object, None);
assert_eq!(req_info.version_id, None);
}
#[tokio::test]
async fn abort_multipart_upload_rejects_unauthorized_request() {
let fs = FS::new();