mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 21:26:28 +00:00
test(e2e): require authorization denial codes (#6497)
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
mod tests {
|
||||
use crate::common::{RustFSTestEnvironment, init_logging, local_http_client, rustfs_binary_path};
|
||||
use aws_sdk_s3::config::{Credentials, Region};
|
||||
use aws_sdk_s3::error::ProvideErrorMetadata;
|
||||
use aws_sdk_s3::{Client, Config};
|
||||
use http::header::HOST;
|
||||
use rustfs_signer::constants::UNSIGNED_PAYLOAD;
|
||||
@@ -368,10 +369,15 @@ mod tests {
|
||||
reqwest::StatusCode::FORBIDDEN,
|
||||
"stale root must be rejected on the admin API after rotation, body: {body}"
|
||||
);
|
||||
let s3_old = s3_client_with(&env, &old_ak, &old_sk).list_buckets().send().await;
|
||||
assert!(
|
||||
s3_old.is_err(),
|
||||
"stale root must be rejected on the S3 plane after rotation, got: {s3_old:?}"
|
||||
let s3_old = s3_client_with(&env, &old_ak, &old_sk)
|
||||
.list_buckets()
|
||||
.send()
|
||||
.await
|
||||
.expect_err("stale root must be rejected on the S3 plane after rotation");
|
||||
assert_eq!(
|
||||
s3_old.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("InvalidAccessKeyId"),
|
||||
"stale root must receive InvalidAccessKeyId after rotation: {s3_old:?}"
|
||||
);
|
||||
|
||||
env.stop_server();
|
||||
|
||||
@@ -30,6 +30,7 @@ use crate::common::{
|
||||
RustFSTestEnvironment, admin_ok, admin_request, admin_request_with_session_token, build_test_sts_client, init_logging,
|
||||
};
|
||||
use aws_sdk_s3::config::{Credentials, Region};
|
||||
use aws_sdk_s3::error::ProvideErrorMetadata;
|
||||
use aws_sdk_s3::primitives::ByteStream;
|
||||
use aws_sdk_s3::{Client, Config};
|
||||
use reqwest::StatusCode;
|
||||
@@ -411,8 +412,13 @@ async fn test_admin_user_policy_service_account_crud_lifecycle() -> TestResult {
|
||||
.key("before-attach")
|
||||
.body(ByteStream::from_static(b"x"))
|
||||
.send()
|
||||
.await;
|
||||
assert!(denied.is_err(), "user without a policy must not be able to write to {bucket}");
|
||||
.await
|
||||
.expect_err("user without a policy must not be able to write to the bucket");
|
||||
assert_eq!(
|
||||
denied.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("AccessDenied"),
|
||||
"user without a policy must receive AccessDenied: {denied:?}"
|
||||
);
|
||||
|
||||
// --- attach policy: the credential actually gains S3 access -----------------
|
||||
admin_ok(
|
||||
@@ -499,13 +505,19 @@ async fn test_admin_user_policy_service_account_crud_lifecycle() -> TestResult {
|
||||
.body(ByteStream::from_static(b"x"))
|
||||
.send()
|
||||
.await;
|
||||
if revoked.is_err() {
|
||||
break;
|
||||
match revoked {
|
||||
Ok(_) if tokio::time::Instant::now() >= deadline => {
|
||||
return Err("deleted service account credential still works".into());
|
||||
}
|
||||
Ok(_) => sleep(Duration::from_millis(500)).await,
|
||||
Err(error) => {
|
||||
let code = error.as_service_error().and_then(ProvideErrorMetadata::code);
|
||||
if matches!(code, Some("AccessDenied" | "InvalidAccessKeyId")) {
|
||||
break;
|
||||
}
|
||||
return Err(format!("deleted service account must fail with an authorization error, got {error:?}").into());
|
||||
}
|
||||
}
|
||||
if tokio::time::Instant::now() >= deadline {
|
||||
return Err("deleted service account credential still works".into());
|
||||
}
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
// Disable then remove the user; the credential must stop working.
|
||||
@@ -525,13 +537,19 @@ async fn test_admin_user_policy_service_account_crud_lifecycle() -> TestResult {
|
||||
.body(ByteStream::from_static(b"x"))
|
||||
.send()
|
||||
.await;
|
||||
if disabled.is_err() {
|
||||
break;
|
||||
match disabled {
|
||||
Ok(_) if tokio::time::Instant::now() >= deadline => {
|
||||
return Err("disabled user credential still works".into());
|
||||
}
|
||||
Ok(_) => sleep(Duration::from_millis(500)).await,
|
||||
Err(error) => {
|
||||
let code = error.as_service_error().and_then(ProvideErrorMetadata::code);
|
||||
if matches!(code, Some("AccessDenied" | "InvalidAccessKeyId")) {
|
||||
break;
|
||||
}
|
||||
return Err(format!("disabled user must fail with an authorization error, got {error:?}").into());
|
||||
}
|
||||
}
|
||||
if tokio::time::Instant::now() >= deadline {
|
||||
return Err("disabled user credential still works".into());
|
||||
}
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
admin_ok(
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
use crate::common::{RustFSTestEnvironment, awscurl_delete, awscurl_post_sts_form_urlencoded, awscurl_put, init_logging};
|
||||
use aws_sdk_s3::config::{Credentials, Region};
|
||||
use aws_sdk_s3::error::ProvideErrorMetadata;
|
||||
use aws_sdk_s3::primitives::ByteStream;
|
||||
use aws_sdk_s3::types::{Delete, ObjectIdentifier, Tag, Tagging};
|
||||
use aws_sdk_s3::{Client, Config};
|
||||
@@ -208,10 +209,17 @@ async fn test_e2e_iam_policy_existing_object_tag_get_object() -> Result<(), Box<
|
||||
let _ = out.body.collect().await?;
|
||||
|
||||
put_object_tag_kv(&admin, &bucket, key, "security", "private").await?;
|
||||
let denied = uclient.get_object().bucket(&bucket).key(key).send().await;
|
||||
assert!(
|
||||
denied.is_err(),
|
||||
"GetObject must be denied when ExistingObjectTag no longer matches IAM policy"
|
||||
let denied = uclient
|
||||
.get_object()
|
||||
.bucket(&bucket)
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
.expect_err("GetObject must be denied when ExistingObjectTag no longer matches IAM policy");
|
||||
assert_eq!(
|
||||
denied.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("AccessDenied"),
|
||||
"IAM ExistingObjectTag mismatch must return AccessDenied: {denied:?}"
|
||||
);
|
||||
|
||||
cleanup_bucket_and_object(&admin, &bucket, key).await;
|
||||
@@ -245,8 +253,13 @@ async fn test_e2e_bucket_policy_existing_object_tag_get_object() -> Result<(), B
|
||||
.bucket(&bucket)
|
||||
.key(key)
|
||||
.send()
|
||||
.await;
|
||||
assert!(deny_before.is_err(), "without bucket policy, user must be denied");
|
||||
.await
|
||||
.expect_err("without bucket policy, user must be denied");
|
||||
assert_eq!(
|
||||
deny_before.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("AccessDenied"),
|
||||
"missing bucket policy must return AccessDenied: {deny_before:?}"
|
||||
);
|
||||
|
||||
let bp = serde_json::json!({
|
||||
"Version": "2012-10-17",
|
||||
@@ -268,8 +281,18 @@ async fn test_e2e_bucket_policy_existing_object_tag_get_object() -> Result<(), B
|
||||
let _ = ok.body.collect().await?;
|
||||
|
||||
put_object_tag_kv(&admin, &bucket, key, "security", "private").await?;
|
||||
let denied = uclient.get_object().bucket(&bucket).key(key).send().await;
|
||||
assert!(denied.is_err(), "GetObject must fail when tag no longer satisfies bucket policy");
|
||||
let denied = uclient
|
||||
.get_object()
|
||||
.bucket(&bucket)
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
.expect_err("GetObject must fail when tag no longer satisfies bucket policy");
|
||||
assert_eq!(
|
||||
denied.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("AccessDenied"),
|
||||
"bucket-policy ExistingObjectTag mismatch must return AccessDenied: {denied:?}"
|
||||
);
|
||||
|
||||
cleanup_bucket_and_object(&admin, &bucket, key).await;
|
||||
admin_remove_user(&env, &user).await;
|
||||
@@ -335,10 +358,17 @@ async fn test_e2e_sts_assume_role_session_policy_existing_object_tag() -> Result
|
||||
let _ = ok.body.collect().await?;
|
||||
|
||||
put_object_tag_kv(&parent_client, &bucket, key, "security", "private").await?;
|
||||
let denied = session_client.get_object().bucket(&bucket).key(key).send().await;
|
||||
assert!(
|
||||
denied.is_err(),
|
||||
"session policy must deny GetObject when ExistingObjectTag no longer matches"
|
||||
let denied = session_client
|
||||
.get_object()
|
||||
.bucket(&bucket)
|
||||
.key(key)
|
||||
.send()
|
||||
.await
|
||||
.expect_err("session policy must deny GetObject when ExistingObjectTag no longer matches");
|
||||
assert_eq!(
|
||||
denied.as_service_error().and_then(ProvideErrorMetadata::code),
|
||||
Some("AccessDenied"),
|
||||
"STS ExistingObjectTag mismatch must return AccessDenied: {denied:?}"
|
||||
);
|
||||
|
||||
cleanup_bucket_and_object(&admin, &bucket, key).await;
|
||||
|
||||
@@ -682,18 +682,26 @@ mod integration_tests {
|
||||
assert!(resp.contains("quota_limit"));
|
||||
|
||||
// Normal user sets quota — should be denied
|
||||
let set_resp = awscurl_put(
|
||||
let set_error = awscurl_put(
|
||||
&get_url,
|
||||
&serde_json::json!({"quota": 2048, "quota_type": "HARD"}).to_string(),
|
||||
normal_ak,
|
||||
normal_sk,
|
||||
)
|
||||
.await;
|
||||
assert!(set_resp.is_err(), "normal user should not be able to set quota");
|
||||
.await
|
||||
.expect_err("normal user should not be able to set quota")
|
||||
.to_string();
|
||||
assert!(set_error.contains("AccessDenied"), "quota denial must return AccessDenied: {set_error}");
|
||||
|
||||
// Normal user clears quota — should be denied
|
||||
let del_resp = awscurl_delete(&get_url, normal_ak, normal_sk).await;
|
||||
assert!(del_resp.is_err(), "normal user should not be able to clear quota");
|
||||
let delete_error = awscurl_delete(&get_url, normal_ak, normal_sk)
|
||||
.await
|
||||
.expect_err("normal user should not be able to clear quota")
|
||||
.to_string();
|
||||
assert!(
|
||||
delete_error.contains("AccessDenied"),
|
||||
"quota deletion denial must return AccessDenied: {delete_error}"
|
||||
);
|
||||
|
||||
env.cleanup_bucket().await?;
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user