mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-25 21:46:50 +00:00
fix(authz): fail closed on policy load errors (#5359)
* fix(authz): fail closed on policy load errors * test(authz): cover policy failure precedence * test: align policy failure expectations * test: align upload part copy fail-closed expectation
This commit is contained in:
@@ -741,6 +741,8 @@ impl BucketMetadataSys {
|
|||||||
|
|
||||||
if let Some(config) = &bm.policy_config {
|
if let Some(config) = &bm.policy_config {
|
||||||
Ok((config.clone(), bm.policy_config_updated_at))
|
Ok((config.clone(), bm.policy_config_updated_at))
|
||||||
|
} else if !bm.policy_config_json.is_empty() {
|
||||||
|
Ok((serde_json::from_slice(&bm.policy_config_json)?, bm.policy_config_updated_at))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::ConfigNotFound)
|
Err(Error::ConfigNotFound)
|
||||||
}
|
}
|
||||||
@@ -1051,6 +1053,22 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_bucket_policy_rejects_malformed_cached_policy() {
|
||||||
|
let (_dirs, ecstore) = isolated_store_over_temp_disks().await;
|
||||||
|
let sys = BucketMetadataSys::new(ecstore);
|
||||||
|
let mut metadata = BucketMetadata::new("malformed-policy");
|
||||||
|
metadata.policy_config_json = b"{".to_vec();
|
||||||
|
sys.set("malformed-policy".to_string(), Arc::new(metadata)).await;
|
||||||
|
|
||||||
|
let err = sys
|
||||||
|
.get_bucket_policy("malformed-policy")
|
||||||
|
.await
|
||||||
|
.expect_err("malformed persisted policy must not be treated as missing");
|
||||||
|
|
||||||
|
assert!(matches!(err, Error::Io(_)), "malformed persisted policy must surface its parse failure");
|
||||||
|
}
|
||||||
|
|
||||||
fn target(bucket: &str, id: &str) -> BucketTarget {
|
fn target(bucket: &str, id: &str) -> BucketTarget {
|
||||||
BucketTarget {
|
BucketTarget {
|
||||||
source_bucket: bucket.to_string(),
|
source_bucket: bucket.to_string(),
|
||||||
|
|||||||
@@ -15,23 +15,26 @@
|
|||||||
use super::metadata_sys::get_bucket_metadata_sys;
|
use super::metadata_sys::get_bucket_metadata_sys;
|
||||||
use crate::error::{Result, StorageError};
|
use crate::error::{Result, StorageError};
|
||||||
use rustfs_policy::policy::{BucketPolicy, BucketPolicyArgs};
|
use rustfs_policy::policy::{BucketPolicy, BucketPolicyArgs};
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
pub struct PolicySys {}
|
pub struct PolicySys {}
|
||||||
|
|
||||||
impl PolicySys {
|
impl PolicySys {
|
||||||
pub async fn is_allowed(args: &BucketPolicyArgs<'_>) -> bool {
|
pub async fn is_allowed(args: &BucketPolicyArgs<'_>) -> bool {
|
||||||
match Self::get(args.bucket).await {
|
matches!(Self::try_is_allowed(args).await, Ok(true))
|
||||||
Ok(cfg) => return cfg.is_allowed(args).await,
|
|
||||||
Err(err) => {
|
|
||||||
if err != StorageError::ConfigNotFound {
|
|
||||||
info!("config get err {:?}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
args.is_owner
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn try_is_allowed(args: &BucketPolicyArgs<'_>) -> Result<bool> {
|
||||||
|
Self::is_allowed_with_policy(args, Self::get(args.bucket).await).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn is_allowed_with_policy(args: &BucketPolicyArgs<'_>, policy: Result<BucketPolicy>) -> Result<bool> {
|
||||||
|
match policy {
|
||||||
|
Ok(policy) => Ok(policy.is_allowed(args).await),
|
||||||
|
Err(StorageError::ConfigNotFound) => Ok(args.is_owner),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get(bucket: &str) -> Result<BucketPolicy> {
|
pub async fn get(bucket: &str) -> Result<BucketPolicy> {
|
||||||
let bucket_meta_sys_lock = get_bucket_metadata_sys()?;
|
let bucket_meta_sys_lock = get_bucket_metadata_sys()?;
|
||||||
let bucket_meta_sys = bucket_meta_sys_lock.read().await;
|
let bucket_meta_sys = bucket_meta_sys_lock.read().await;
|
||||||
@@ -41,3 +44,91 @@ impl PolicySys {
|
|||||||
Ok(cfg)
|
Ok(cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{PolicySys, StorageError};
|
||||||
|
use rustfs_policy::policy::action::{Action, S3Action};
|
||||||
|
use rustfs_policy::policy::{BucketPolicy, BucketPolicyArgs};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
fn args<'a>(
|
||||||
|
is_owner: bool,
|
||||||
|
groups: &'a Option<Vec<String>>,
|
||||||
|
conditions: &'a HashMap<String, Vec<String>>,
|
||||||
|
) -> BucketPolicyArgs<'a> {
|
||||||
|
BucketPolicyArgs {
|
||||||
|
bucket: "bucket",
|
||||||
|
action: Action::S3Action(S3Action::GetObjectAction),
|
||||||
|
is_owner,
|
||||||
|
account: "account",
|
||||||
|
groups,
|
||||||
|
conditions,
|
||||||
|
object: "object",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn missing_policy_preserves_owner_and_iam_fallback_semantics() {
|
||||||
|
let groups = None;
|
||||||
|
let conditions = HashMap::new();
|
||||||
|
assert!(
|
||||||
|
PolicySys::is_allowed_with_policy(&args(true, &groups, &conditions), Err(StorageError::ConfigNotFound),)
|
||||||
|
.await
|
||||||
|
.expect("missing policy should preserve owner access")
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!PolicySys::is_allowed_with_policy(&args(false, &groups, &conditions), Err(StorageError::ConfigNotFound),)
|
||||||
|
.await
|
||||||
|
.expect("missing policy should defer non-owner access to IAM")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn policy_load_failures_propagate() {
|
||||||
|
let groups = None;
|
||||||
|
let conditions = HashMap::new();
|
||||||
|
for (failure, expected_message) in [
|
||||||
|
(StorageError::Io(std::io::Error::other("policy read failed")), "policy read failed"),
|
||||||
|
(
|
||||||
|
StorageError::other("bucket metadata sys not initialized for this instance"),
|
||||||
|
"bucket metadata sys not initialized for this instance",
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
let result = PolicySys::is_allowed_with_policy(&args(true, &groups, &conditions), Err(failure)).await;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(result, Err(StorageError::Io(ref err)) if err.to_string().contains(expected_message)),
|
||||||
|
"policy I/O and uninitialized metadata failures must propagate instead of granting owner access"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn explicit_bucket_deny_precedes_iam_allow() {
|
||||||
|
let groups = None;
|
||||||
|
let conditions = HashMap::new();
|
||||||
|
let policy: BucketPolicy = serde_json::from_str(
|
||||||
|
r#"{
|
||||||
|
"Version":"2012-10-17",
|
||||||
|
"Statement":[{
|
||||||
|
"Effect":"Deny",
|
||||||
|
"Principal":{"AWS":"*"},
|
||||||
|
"Action":["s3:GetObject"],
|
||||||
|
"Resource":["arn:aws:s3:::bucket/*"]
|
||||||
|
}]
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.expect("deny policy should parse");
|
||||||
|
|
||||||
|
let bucket_allowed = PolicySys::is_allowed_with_policy(&args(true, &groups, &conditions), Ok(policy))
|
||||||
|
.await
|
||||||
|
.expect("loaded bucket policy should evaluate");
|
||||||
|
let iam_allowed = true;
|
||||||
|
let request_allowed = bucket_allowed && iam_allowed;
|
||||||
|
|
||||||
|
assert!(iam_allowed, "test precondition: IAM grants the action");
|
||||||
|
assert!(!bucket_allowed, "test precondition: bucket policy explicitly denies the action");
|
||||||
|
assert!(!request_allowed, "explicit bucket Deny must reject before IAM Allow fallback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1810,7 +1810,7 @@ impl DefaultBucketUsecase {
|
|||||||
client_info,
|
client_info,
|
||||||
);
|
);
|
||||||
|
|
||||||
let read_allowed = PolicySys::is_allowed(&BucketPolicyArgs {
|
let read_allowed = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: &bucket,
|
bucket: &bucket,
|
||||||
action: Action::S3Action(S3Action::ListBucketAction),
|
action: Action::S3Action(S3Action::ListBucketAction),
|
||||||
is_owner: false,
|
is_owner: false,
|
||||||
@@ -1819,9 +1819,10 @@ impl DefaultBucketUsecase {
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: "",
|
object: "",
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.map_err(ApiError::from)?;
|
||||||
|
|
||||||
let write_allowed = PolicySys::is_allowed(&BucketPolicyArgs {
|
let write_allowed = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: &bucket,
|
bucket: &bucket,
|
||||||
action: Action::S3Action(S3Action::PutObjectAction),
|
action: Action::S3Action(S3Action::PutObjectAction),
|
||||||
is_owner: false,
|
is_owner: false,
|
||||||
@@ -1830,7 +1831,8 @@ impl DefaultBucketUsecase {
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: "",
|
object: "",
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.map_err(ApiError::from)?;
|
||||||
|
|
||||||
let mut is_public = read_allowed || write_allowed;
|
let mut is_public = read_allowed || write_allowed;
|
||||||
let ignore_public_acls = match metadata_sys::get_public_access_block_config(&bucket).await {
|
let ignore_public_acls = match metadata_sys::get_public_access_block_config(&bucket).await {
|
||||||
|
|||||||
@@ -494,6 +494,14 @@ mod tests {
|
|||||||
assert!(downcast_storage_error.is_some());
|
assert!(downcast_storage_error.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn policy_metadata_read_failure_maps_to_internal_error() {
|
||||||
|
let api_error = ApiError::from(StorageError::Io(IoError::other("policy read failed")));
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_kms_service_unavailable_maps_to_retryable_error() {
|
fn test_kms_service_unavailable_maps_to_retryable_error() {
|
||||||
let api_error = ApiError::from(StorageError::other(KmsUnavailableError));
|
let api_error = ApiError::from(StorageError::other(KmsUnavailableError));
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
let owner_can_bypass_deny = owner_can_bypass_policy_deny(is_owner, &action);
|
let owner_can_bypass_deny = owner_can_bypass_policy_deny(is_owner, &action);
|
||||||
if !bucket_name.is_empty()
|
if !bucket_name.is_empty()
|
||||||
&& !owner_can_bypass_deny
|
&& !owner_can_bypass_deny
|
||||||
&& !PolicySys::is_allowed(&BucketPolicyArgs {
|
&& !PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket_name,
|
bucket: bucket_name,
|
||||||
action,
|
action,
|
||||||
// Early explicit-deny gate for bucket policy: use owner short-circuit path so
|
// Early explicit-deny gate for bucket policy: use owner short-circuit path so
|
||||||
@@ -517,6 +517,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(ApiError::from)?
|
||||||
{
|
{
|
||||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||||
}
|
}
|
||||||
@@ -535,7 +536,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
};
|
};
|
||||||
let delete_version_allowed = iam_store.eval_prepared(&prepared, &delete_version_args).await;
|
let delete_version_allowed = iam_store.eval_prepared(&prepared, &delete_version_args).await;
|
||||||
if !delete_version_allowed
|
if !delete_version_allowed
|
||||||
&& !PolicySys::is_allowed(&BucketPolicyArgs {
|
&& !PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action: Action::S3Action(S3Action::DeleteObjectVersionAction),
|
action: Action::S3Action(S3Action::DeleteObjectVersionAction),
|
||||||
is_owner,
|
is_owner,
|
||||||
@@ -545,6 +546,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(ApiError::from)?
|
||||||
{
|
{
|
||||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||||
}
|
}
|
||||||
@@ -571,7 +573,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let policy_allowed_fallback = PolicySys::is_allowed(&BucketPolicyArgs {
|
let policy_allowed_fallback = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action,
|
action,
|
||||||
is_owner,
|
is_owner,
|
||||||
@@ -580,7 +582,8 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.map_err(ApiError::from)?;
|
||||||
|
|
||||||
if policy_allowed_fallback {
|
if policy_allowed_fallback {
|
||||||
authorize_table_data_plane_if_needed(action, bucket.as_str(), object.as_str(), cred, is_owner, &conditions, claims)
|
authorize_table_data_plane_if_needed(action, bucket.as_str(), object.as_str(), cred, is_owner, &conditions, claims)
|
||||||
@@ -605,7 +608,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if PolicySys::is_allowed(&BucketPolicyArgs {
|
if PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action: Action::S3Action(S3Action::ListBucketAction),
|
action: Action::S3Action(S3Action::ListBucketAction),
|
||||||
is_owner,
|
is_owner,
|
||||||
@@ -615,6 +618,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(ApiError::from)?
|
||||||
{
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -688,7 +692,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
let bucket_name = bucket.as_str();
|
let bucket_name = bucket.as_str();
|
||||||
|
|
||||||
if !bucket_name.is_empty()
|
if !bucket_name.is_empty()
|
||||||
&& !PolicySys::is_allowed(&BucketPolicyArgs {
|
&& !PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket_name,
|
bucket: bucket_name,
|
||||||
action,
|
action,
|
||||||
// Early explicit-deny gate for bucket policy in anonymous path.
|
// Early explicit-deny gate for bucket policy in anonymous path.
|
||||||
@@ -699,13 +703,14 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(ApiError::from)?
|
||||||
{
|
{
|
||||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if action != Action::S3Action(S3Action::ListAllMyBucketsAction) {
|
if action != Action::S3Action(S3Action::ListAllMyBucketsAction) {
|
||||||
if action == Action::S3Action(S3Action::DeleteObjectAction) && version_id.is_some() {
|
if action == Action::S3Action(S3Action::DeleteObjectAction) && version_id.is_some() {
|
||||||
let delete_version_allowed = PolicySys::is_allowed(&BucketPolicyArgs {
|
let delete_version_allowed = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action: Action::S3Action(S3Action::DeleteObjectVersionAction),
|
action: Action::S3Action(S3Action::DeleteObjectVersionAction),
|
||||||
is_owner: false,
|
is_owner: false,
|
||||||
@@ -714,13 +719,14 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.map_err(ApiError::from)?;
|
||||||
if !delete_version_allowed {
|
if !delete_version_allowed {
|
||||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let policy_allowed = PolicySys::is_allowed(&BucketPolicyArgs {
|
let policy_allowed = PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action,
|
action,
|
||||||
is_owner: false,
|
is_owner: false,
|
||||||
@@ -729,7 +735,8 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: object.as_str(),
|
object: object.as_str(),
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.map_err(ApiError::from)?;
|
||||||
|
|
||||||
// A bucket policy granting s3:ListBucket also covers listing versions. This
|
// A bucket policy granting s3:ListBucket also covers listing versions. This
|
||||||
// fallback has to feed the same post-authorization gates as the direct grant
|
// fallback has to feed the same post-authorization gates as the direct grant
|
||||||
@@ -737,7 +744,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
// ListObjectVersions after RestrictPublicBuckets is turned on.
|
// ListObjectVersions after RestrictPublicBuckets is turned on.
|
||||||
let policy_allowed = policy_allowed
|
let policy_allowed = policy_allowed
|
||||||
|| (action == Action::S3Action(S3Action::ListBucketVersionsAction)
|
|| (action == Action::S3Action(S3Action::ListBucketVersionsAction)
|
||||||
&& PolicySys::is_allowed(&BucketPolicyArgs {
|
&& PolicySys::try_is_allowed(&BucketPolicyArgs {
|
||||||
bucket: bucket.as_str(),
|
bucket: bucket.as_str(),
|
||||||
action: Action::S3Action(S3Action::ListBucketAction),
|
action: Action::S3Action(S3Action::ListBucketAction),
|
||||||
is_owner: false,
|
is_owner: false,
|
||||||
@@ -746,7 +753,8 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
conditions: &conditions,
|
conditions: &conditions,
|
||||||
object: "",
|
object: "",
|
||||||
})
|
})
|
||||||
.await);
|
.await
|
||||||
|
.map_err(ApiError::from)?);
|
||||||
|
|
||||||
if policy_allowed {
|
if policy_allowed {
|
||||||
deny_anonymous_table_data_plane_if_needed(action, bucket.as_str(), object.as_str()).await?;
|
deny_anonymous_table_data_plane_if_needed(action, bucket.as_str(), object.as_str()).await?;
|
||||||
@@ -2763,7 +2771,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn abort_multipart_upload_rejects_unauthorized_request() {
|
async fn abort_multipart_upload_fails_closed_when_policy_metadata_is_unavailable() {
|
||||||
let fs = FS::new();
|
let fs = FS::new();
|
||||||
let mut req = build_request(
|
let mut req = build_request(
|
||||||
AbortMultipartUploadInput::builder()
|
AbortMultipartUploadInput::builder()
|
||||||
@@ -2779,12 +2787,12 @@ mod tests {
|
|||||||
let err = fs
|
let err = fs
|
||||||
.abort_multipart_upload(&mut req)
|
.abort_multipart_upload(&mut req)
|
||||||
.await
|
.await
|
||||||
.expect_err("missing credentials should reject access");
|
.expect_err("unavailable policy metadata must fail closed");
|
||||||
assert_eq!(err.code(), &S3ErrorCode::AccessDenied);
|
assert_eq!(err.code(), &S3ErrorCode::InternalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn complete_multipart_upload_rejects_unauthorized_request() {
|
async fn complete_multipart_upload_fails_closed_when_policy_metadata_is_unavailable() {
|
||||||
let fs = FS::new();
|
let fs = FS::new();
|
||||||
let mut req = build_request(
|
let mut req = build_request(
|
||||||
CompleteMultipartUploadInput::builder()
|
CompleteMultipartUploadInput::builder()
|
||||||
@@ -2801,12 +2809,12 @@ mod tests {
|
|||||||
let err = fs
|
let err = fs
|
||||||
.complete_multipart_upload(&mut req)
|
.complete_multipart_upload(&mut req)
|
||||||
.await
|
.await
|
||||||
.expect_err("missing credentials should reject access");
|
.expect_err("unavailable policy metadata must fail closed");
|
||||||
assert_eq!(err.code(), &S3ErrorCode::AccessDenied);
|
assert_eq!(err.code(), &S3ErrorCode::InternalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn upload_part_copy_rejects_unauthorized_request() {
|
async fn upload_part_copy_fails_closed_when_policy_metadata_is_unavailable() {
|
||||||
let fs = FS::new();
|
let fs = FS::new();
|
||||||
let mut req = build_request(
|
let mut req = build_request(
|
||||||
UploadPartCopyInput::builder()
|
UploadPartCopyInput::builder()
|
||||||
@@ -2828,7 +2836,7 @@ mod tests {
|
|||||||
let err = fs
|
let err = fs
|
||||||
.upload_part_copy(&mut req)
|
.upload_part_copy(&mut req)
|
||||||
.await
|
.await
|
||||||
.expect_err("missing credentials should reject access");
|
.expect_err("unavailable policy metadata must fail closed");
|
||||||
assert_eq!(err.code(), &S3ErrorCode::AccessDenied);
|
assert_eq!(err.code(), &S3ErrorCode::InternalError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user