fix(policy): support version ID condition keys (#5810)

This commit is contained in:
cxymds
2026-08-08 05:42:52 +08:00
committed by GitHub
parent 6633c80151
commit ce7ca4cbb8
4 changed files with 58 additions and 0 deletions
@@ -6028,6 +6028,33 @@ async fn test_signed_put_object_extract_authorizes_each_pax_privilege_and_retent
}
let version_condition_client = restricted_user_client(&env, version_condition_user, version_condition_secret);
let mismatching_version_pax = HashMap::from([("minio.versionId", Uuid::new_v4().to_string())]);
let archive = make_tar_with_pax_entry("version-mismatch-entry.txt", b"must-not-write", None, &mismatching_version_pax).await;
let err = version_condition_client
.put_object()
.bucket(bucket)
.key("version-mismatch.tar")
.body(ByteStream::from(archive))
.customize()
.mutate_request(|req| {
req.headers_mut().insert("x-amz-meta-snowball-auto-extract", "true");
})
.send()
.await
.expect_err("a mismatching PAX version ID must fail the replication condition");
assert_eq!(err.as_service_error().and_then(|error| error.meta().code()), Some("AccessDenied"));
let err = admin_client
.head_object()
.bucket(bucket)
.key("version-mismatch-entry.txt")
.send()
.await
.expect_err("a denied PAX entry must not be written");
assert!(matches!(
err.as_service_error().and_then(|error| error.meta().code()),
Some("NoSuchKey" | "NotFound")
));
let matching_version_pax = HashMap::from([("minio.versionId", conditional_version_id)]);
let archive = make_tar_with_pax_entry("condition-entry.txt", b"condition-body", None, &matching_version_pax).await;
version_condition_client
@@ -6041,6 +6068,13 @@ async fn test_signed_put_object_extract_authorizes_each_pax_privilege_and_retent
})
.send()
.await?;
let stored = admin_client
.get_object()
.bucket(bucket)
.key("condition-entry.txt")
.send()
.await?;
assert_eq!(stored.body.collect().await?.into_bytes().as_ref(), b"condition-body");
let pax_context_client = restricted_user_client(&env, pax_context_user, pax_context_secret);
let tag_pax = HashMap::from([("minio.metadata.x-amz-tagging", "classification=public".to_string())]);
@@ -56,6 +56,7 @@ impl KeyName {
KeyName::S3(S3KeyName::S3SignatureAge),
KeyName::S3(S3KeyName::S3XAmzContentSha256),
KeyName::S3(S3KeyName::S3LocationConstraint),
KeyName::S3(S3KeyName::S3VersionId),
//aws
KeyName::Aws(AwsKeyName::AWSReferer),
KeyName::Aws(AwsKeyName::AWSSourceIP),
@@ -191,6 +192,9 @@ pub enum S3KeyName {
#[strum(serialize = "s3:LocationConstraint")]
S3LocationConstraint,
#[strum(to_string = "s3:versionid", serialize = "s3:VersionId")]
S3VersionId,
#[strum(serialize = "s3:object-lock-retain-until-date")]
S3ObjectLockRetainUntilDate,
@@ -379,6 +383,8 @@ mod tests {
use test_case::test_case;
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("s3:VersionId", KeyName::S3(S3KeyName::S3VersionId) ; "aws_version_id")]
#[test_case("s3:versionid", KeyName::S3(S3KeyName::S3VersionId) ; "minio_version_id")]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::User))]
@@ -399,6 +405,8 @@ mod tests {
}
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("s3:VersionId", KeyName::S3(S3KeyName::S3VersionId) ; "aws_version_id")]
#[test_case("s3:versionid", KeyName::S3(S3KeyName::S3VersionId) ; "minio_version_id")]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::User))]
@@ -416,6 +424,7 @@ mod tests {
}
#[test_case("s3:x-amz-copy-source", KeyName::S3(S3KeyName::S3XAmzCopySource))]
#[test_case("s3:versionid", KeyName::S3(S3KeyName::S3VersionId))]
#[test_case("aws:SecureTransport", KeyName::Aws(AwsKeyName::AWSSecureTransport))]
#[test_case("jwt:sub", KeyName::Jwt(JwtKeyName::JWTSub))]
#[test_case("ldap:user", KeyName::Ldap(LdapKeyName::User))]
@@ -408,6 +408,8 @@ mod tests {
#[test_case(new_fkv("s3:ExistingObjectTag/security", vec!["public"]), false, vec![("ExistingObjectTag/security", vec!["public"])] => true ; "19")]
#[test_case(new_fkv("s3:ExistingObjectTag/security", vec!["public"]), false, vec![("ExistingObjectTag/security", vec!["private"])] => false ; "20")]
#[test_case(new_fkv("s3:ExistingObjectTag/security", vec!["public"]), false, vec![("ExistingObjectTag/project", vec!["webapp"])] => false ; "21")]
#[test_case(new_fkv("s3:VersionId", vec!["version-1"]), false, vec![("versionid", vec!["version-1"])] => true ; "aws_version_id")]
#[test_case(new_fkv("s3:versionid", vec!["version-1"]), false, vec![("versionid", vec!["version-1"])] => true ; "minio_version_id")]
fn test_string_equals(s: FuncKeyValue<StringFuncValue>, for_all: bool, values: Vec<(&str, Vec<&str>)>) -> bool {
test_eval(s, for_all, false, false, values)
}