mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-19 02:56:18 +00:00
fix: support query-only presigned URL access (#2046)
This commit is contained in:
@@ -21,7 +21,8 @@ use crate::storage::ecfs::RUSTFS_OWNER;
|
|||||||
use crate::storage::entity;
|
use crate::storage::entity;
|
||||||
use crate::storage::helper::OperationHelper;
|
use crate::storage::helper::OperationHelper;
|
||||||
use crate::storage::options::{
|
use crate::storage::options::{
|
||||||
copy_src_opts, extract_metadata, get_complete_multipart_upload_opts, get_content_sha256, parse_copy_source_range, put_opts,
|
copy_src_opts, extract_metadata, get_complete_multipart_upload_opts, get_content_sha256_with_query, parse_copy_source_range,
|
||||||
|
put_opts,
|
||||||
};
|
};
|
||||||
use crate::storage::*;
|
use crate::storage::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@@ -615,7 +616,7 @@ impl DefaultMultipartUsecase {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sha256hex = get_content_sha256(&req.headers);
|
let mut sha256hex = get_content_sha256_with_query(&req.headers, req.uri.query());
|
||||||
|
|
||||||
if is_compressible {
|
if is_compressible {
|
||||||
let mut hrd = HashReader::new(reader, size, actual_size, md5hex, sha256hex, false).map_err(ApiError::from)?;
|
let mut hrd = HashReader::new(reader, size, actual_size, md5hex, sha256hex, false).map_err(ApiError::from)?;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use crate::storage::head_prefix::{head_prefix_not_found_message, probe_prefix_ha
|
|||||||
use crate::storage::helper::OperationHelper;
|
use crate::storage::helper::OperationHelper;
|
||||||
use crate::storage::options::{
|
use crate::storage::options::{
|
||||||
copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime_with_object_name,
|
copy_dst_opts, copy_src_opts, del_opts, extract_metadata, extract_metadata_from_mime_with_object_name,
|
||||||
filter_object_metadata, get_content_sha256, get_opts, put_opts,
|
filter_object_metadata, get_content_sha256_with_query, get_opts, put_opts,
|
||||||
};
|
};
|
||||||
use crate::storage::s3_api::multipart::parse_list_parts_params;
|
use crate::storage::s3_api::multipart::parse_list_parts_params;
|
||||||
use crate::storage::s3_api::{restore, select};
|
use crate::storage::s3_api::{restore, select};
|
||||||
@@ -461,7 +461,7 @@ impl DefaultObjectUsecase {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sha256hex = get_content_sha256(&req.headers);
|
let mut sha256hex = get_content_sha256_with_query(&req.headers, req.uri.query());
|
||||||
|
|
||||||
if is_compressible(&req.headers, &key) && size > MIN_COMPRESSIBLE_SIZE as i64 {
|
if is_compressible(&req.headers, &key) && size > MIN_COMPRESSIBLE_SIZE as i64 {
|
||||||
let algorithm = CompressionAlgorithm::default();
|
let algorithm = CompressionAlgorithm::default();
|
||||||
@@ -3521,7 +3521,7 @@ impl DefaultObjectUsecase {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let sha256hex = get_content_sha256(&req.headers);
|
let sha256hex = get_content_sha256_with_query(&req.headers, req.uri.query());
|
||||||
let actual_size = size;
|
let actual_size = size;
|
||||||
|
|
||||||
let reader: Box<dyn Reader> = Box::new(WarpReader::new(body));
|
let reader: Box<dyn Reader> = Box::new(WarpReader::new(body));
|
||||||
|
|||||||
+90
-8
@@ -397,6 +397,29 @@ pub fn get_condition_values(
|
|||||||
version_id: Option<&str>,
|
version_id: Option<&str>,
|
||||||
region: Option<s3s::region::Region>,
|
region: Option<s3s::region::Region>,
|
||||||
remote_addr: Option<std::net::SocketAddr>,
|
remote_addr: Option<std::net::SocketAddr>,
|
||||||
|
) -> HashMap<String, Vec<String>> {
|
||||||
|
get_condition_values_with_query(header, cred, version_id, region, remote_addr, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get condition values for policy evaluation with optional query-string values.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `header` - HTTP headers of the request
|
||||||
|
/// * `cred` - User credentials
|
||||||
|
/// * `version_id` - Optional version ID of the object
|
||||||
|
/// * `region` - Optional region/location constraint
|
||||||
|
/// * `remote_addr` - Optional remote address of the connection
|
||||||
|
/// * `query` - Optional request query string
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * `HashMap<String, Vec<String>>` - Condition values for policy evaluation
|
||||||
|
pub fn get_condition_values_with_query(
|
||||||
|
header: &HeaderMap,
|
||||||
|
cred: &Credentials,
|
||||||
|
version_id: Option<&str>,
|
||||||
|
region: Option<s3s::region::Region>,
|
||||||
|
remote_addr: Option<std::net::SocketAddr>,
|
||||||
|
query: Option<&str>,
|
||||||
) -> HashMap<String, Vec<String>> {
|
) -> HashMap<String, Vec<String>> {
|
||||||
let username = if cred.is_temp() || cred.is_service_account() {
|
let username = if cred.is_temp() || cred.is_service_account() {
|
||||||
cred.parent_user.clone()
|
cred.parent_user.clone()
|
||||||
@@ -427,8 +450,8 @@ pub fn get_condition_values(
|
|||||||
// Use provided version ID or empty string
|
// Use provided version ID or empty string
|
||||||
let vid = version_id.unwrap_or("");
|
let vid = version_id.unwrap_or("");
|
||||||
|
|
||||||
// Determine auth type and signature version from headers
|
// Determine auth type and signature version from headers and query
|
||||||
let (auth_type, signature_version) = determine_auth_type_and_version(header);
|
let (auth_type, signature_version) = determine_auth_type_and_version_with_query(header, query);
|
||||||
|
|
||||||
// Get TLS status from header
|
// Get TLS status from header
|
||||||
let is_tls = header
|
let is_tls = header
|
||||||
@@ -582,10 +605,16 @@ pub fn get_condition_values(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `AuthType` - The determined authentication type
|
/// * `AuthType` - The determined authentication type
|
||||||
///
|
///
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn get_request_auth_type(header: &HeaderMap) -> AuthType {
|
pub fn get_request_auth_type(header: &HeaderMap) -> AuthType {
|
||||||
|
get_request_auth_type_with_query(header, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn get_request_auth_type_with_query(header: &HeaderMap, query: Option<&str>) -> AuthType {
|
||||||
if is_request_signature_v2(header) {
|
if is_request_signature_v2(header) {
|
||||||
AuthType::SignedV2
|
AuthType::SignedV2
|
||||||
} else if is_request_presigned_signature_v2(header) {
|
} else if is_request_presigned_signature_v2(header, query) {
|
||||||
AuthType::PresignedV2
|
AuthType::PresignedV2
|
||||||
} else if is_request_sign_streaming_v4(header) {
|
} else if is_request_sign_streaming_v4(header) {
|
||||||
AuthType::StreamingSigned
|
AuthType::StreamingSigned
|
||||||
@@ -595,7 +624,7 @@ pub fn get_request_auth_type(header: &HeaderMap) -> AuthType {
|
|||||||
AuthType::StreamingUnsignedTrailer
|
AuthType::StreamingUnsignedTrailer
|
||||||
} else if is_request_signature_v4(header) {
|
} else if is_request_signature_v4(header) {
|
||||||
AuthType::Signed
|
AuthType::Signed
|
||||||
} else if is_request_presigned_signature_v4(header) {
|
} else if is_request_presigned_signature_v4_with_query(header, query) {
|
||||||
AuthType::Presigned
|
AuthType::Presigned
|
||||||
} else if is_request_jwt(header) {
|
} else if is_request_jwt(header) {
|
||||||
AuthType::JWT
|
AuthType::JWT
|
||||||
@@ -618,8 +647,14 @@ pub fn get_request_auth_type(header: &HeaderMap) -> AuthType {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `(String, String)` - Tuple of auth type and signature version
|
/// * `(String, String)` - Tuple of auth type and signature version
|
||||||
///
|
///
|
||||||
|
#[allow(dead_code)]
|
||||||
fn determine_auth_type_and_version(header: &HeaderMap) -> (String, String) {
|
fn determine_auth_type_and_version(header: &HeaderMap) -> (String, String) {
|
||||||
match get_request_auth_type(header) {
|
determine_auth_type_and_version_with_query(header, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn determine_auth_type_and_version_with_query(header: &HeaderMap, query: Option<&str>) -> (String, String) {
|
||||||
|
match get_request_auth_type_with_query(header, query) {
|
||||||
AuthType::JWT => ("JWT".to_string(), String::new()),
|
AuthType::JWT => ("JWT".to_string(), String::new()),
|
||||||
AuthType::SignedV2 => ("REST-HEADER".to_string(), "AWS2".to_string()),
|
AuthType::SignedV2 => ("REST-HEADER".to_string(), "AWS2".to_string()),
|
||||||
AuthType::PresignedV2 => ("REST-QUERY-STRING".to_string(), "AWS2".to_string()),
|
AuthType::PresignedV2 => ("REST-QUERY-STRING".to_string(), "AWS2".to_string()),
|
||||||
@@ -690,11 +725,18 @@ fn is_request_signature_v2(header: &HeaderMap) -> bool {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `bool` - True if request has AWS PreSign Version '4', false otherwise
|
/// * `bool` - True if request has AWS PreSign Version '4', false otherwise
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn is_request_presigned_signature_v4(header: &HeaderMap) -> bool {
|
pub(crate) fn is_request_presigned_signature_v4(header: &HeaderMap) -> bool {
|
||||||
|
is_request_presigned_signature_v4_with_query(header, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_request_presigned_signature_v4_with_query(header: &HeaderMap, query: Option<&str>) -> bool {
|
||||||
if let Some(credential) = header.get(AMZ_CREDENTIAL) {
|
if let Some(credential) = header.get(AMZ_CREDENTIAL) {
|
||||||
return !credential.to_str().unwrap_or("").is_empty();
|
return !credential.to_str().unwrap_or("").is_empty();
|
||||||
}
|
}
|
||||||
false
|
query
|
||||||
|
.and_then(|query| get_query_param(query, "x-amz-credential"))
|
||||||
|
.is_some_and(|credential| !credential.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify request has AWS PreSign Version '2'
|
/// Verify request has AWS PreSign Version '2'
|
||||||
@@ -704,11 +746,13 @@ pub(crate) fn is_request_presigned_signature_v4(header: &HeaderMap) -> bool {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// * `bool` - True if request has AWS PreSign Version '2', false otherwise
|
/// * `bool` - True if request has AWS PreSign Version '2', false otherwise
|
||||||
fn is_request_presigned_signature_v2(header: &HeaderMap) -> bool {
|
fn is_request_presigned_signature_v2(header: &HeaderMap, query: Option<&str>) -> bool {
|
||||||
if let Some(access_key) = header.get(AMZ_ACCESS_KEY_ID) {
|
if let Some(access_key) = header.get(AMZ_ACCESS_KEY_ID) {
|
||||||
return !access_key.to_str().unwrap_or("").is_empty();
|
return !access_key.to_str().unwrap_or("").is_empty();
|
||||||
}
|
}
|
||||||
false
|
query
|
||||||
|
.and_then(|query| get_query_param(query, "awsaccesskeyid"))
|
||||||
|
.is_some_and(|access_key| !access_key.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify if request has AWS Post policy Signature Version '4'
|
/// Verify if request has AWS Post policy Signature Version '4'
|
||||||
@@ -1016,6 +1060,20 @@ mod tests {
|
|||||||
assert_eq!(conditions.get("principaltype"), Some(&vec!["User".to_string()]));
|
assert_eq!(conditions.get("principaltype"), Some(&vec!["User".to_string()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_condition_values_with_presigned_query() {
|
||||||
|
let cred = create_test_credentials();
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
let uri: Uri = "https://example.com/?X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let conditions = get_condition_values_with_query(&headers, &cred, None, None, None, uri.query());
|
||||||
|
|
||||||
|
assert_eq!(conditions.get("signatureversion"), Some(&vec!["AWS4-HMAC-SHA256".to_string()]));
|
||||||
|
assert_eq!(conditions.get("authType"), Some(&vec!["REST-QUERY-STRING".to_string()]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_condition_values_temp_user() {
|
fn test_get_condition_values_temp_user() {
|
||||||
let cred = create_temp_credentials();
|
let cred = create_temp_credentials();
|
||||||
@@ -1284,6 +1342,18 @@ mod tests {
|
|||||||
assert_eq!(auth_type, AuthType::PresignedV2);
|
assert_eq!(auth_type, AuthType::PresignedV2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_request_auth_type_presigned_v2_from_query() {
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
let uri: Uri = "https://example.com/?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=example&Expires=1672531200"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let auth_type = get_request_auth_type_with_query(&headers, uri.query());
|
||||||
|
|
||||||
|
assert_eq!(auth_type, AuthType::PresignedV2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_request_auth_type_presigned_v4() {
|
fn test_get_request_auth_type_presigned_v4() {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
@@ -1297,6 +1367,18 @@ mod tests {
|
|||||||
assert_eq!(auth_type, AuthType::Presigned);
|
assert_eq!(auth_type, AuthType::Presigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_request_auth_type_presigned_v4_from_query() {
|
||||||
|
let headers = HeaderMap::new();
|
||||||
|
let uri: Uri = "https://example.com/?X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request"
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let auth_type = get_request_auth_type_with_query(&headers, uri.query());
|
||||||
|
|
||||||
|
assert_eq!(auth_type, AuthType::Presigned);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_request_auth_type_post_policy() {
|
fn test_get_request_auth_type_post_policy() {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use super::ecfs::{
|
|||||||
stored_acl_from_canned_object,
|
stored_acl_from_canned_object,
|
||||||
};
|
};
|
||||||
use super::options::get_opts;
|
use super::options::get_opts;
|
||||||
use crate::auth::{check_key_valid, get_condition_values, get_session_token};
|
use crate::auth::{check_key_valid, get_condition_values_with_query, get_session_token};
|
||||||
use crate::error::ApiError;
|
use crate::error::ApiError;
|
||||||
use crate::license::license_check;
|
use crate::license::license_check;
|
||||||
use crate::server::RemoteAddr;
|
use crate::server::RemoteAddr;
|
||||||
@@ -276,7 +276,14 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
|
|
||||||
let default_claims = HashMap::new();
|
let default_claims = HashMap::new();
|
||||||
let claims = cred.claims.as_ref().unwrap_or(&default_claims);
|
let claims = cred.claims.as_ref().unwrap_or(&default_claims);
|
||||||
let mut conditions = get_condition_values(&req.headers, cred, req_info.version_id.as_deref(), None, remote_addr);
|
let mut conditions = get_condition_values_with_query(
|
||||||
|
&req.headers,
|
||||||
|
cred,
|
||||||
|
req_info.version_id.as_deref(),
|
||||||
|
None,
|
||||||
|
remote_addr,
|
||||||
|
req.uri.query(),
|
||||||
|
);
|
||||||
// Merge object tag conditions; extend existing values if the same key exists (e.g. from get_condition_values).
|
// Merge object tag conditions; extend existing values if the same key exists (e.g. from get_condition_values).
|
||||||
if let Some(ref tags) = object_tag_conditions {
|
if let Some(ref tags) = object_tag_conditions {
|
||||||
for (k, v) in &tags.0 {
|
for (k, v) in &tags.0 {
|
||||||
@@ -425,12 +432,13 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut conditions = get_condition_values(
|
let mut conditions = get_condition_values_with_query(
|
||||||
&req.headers,
|
&req.headers,
|
||||||
&rustfs_credentials::Credentials::default(),
|
&rustfs_credentials::Credentials::default(),
|
||||||
req_info.version_id.as_deref(),
|
req_info.version_id.as_deref(),
|
||||||
req.region.clone(),
|
req.region.clone(),
|
||||||
remote_addr,
|
remote_addr,
|
||||||
|
req.uri.query(),
|
||||||
);
|
);
|
||||||
// Merge object tag conditions; extend existing values if the same key exists.
|
// Merge object tag conditions; extend existing values if the same key exists.
|
||||||
if let Some(ref tags) = object_tag_conditions {
|
if let Some(ref tags) = object_tag_conditions {
|
||||||
|
|||||||
@@ -44,8 +44,9 @@ use tracing::error;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::auth::AuthType;
|
use crate::auth::AuthType;
|
||||||
use crate::auth::get_request_auth_type;
|
use crate::auth::get_query_param;
|
||||||
use crate::auth::is_request_presigned_signature_v4;
|
use crate::auth::get_request_auth_type_with_query;
|
||||||
|
use crate::auth::is_request_presigned_signature_v4_with_query;
|
||||||
|
|
||||||
const REPLICATION_REQUEST_TRUE: HeaderValue = HeaderValue::from_static("true");
|
const REPLICATION_REQUEST_TRUE: HeaderValue = HeaderValue::from_static("true");
|
||||||
|
|
||||||
@@ -580,13 +581,18 @@ pub fn parse_copy_source_range(range_str: &str) -> S3Result<HTTPRangeSpec> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn get_content_sha256(headers: &HeaderMap<HeaderValue>) -> Option<String> {
|
pub(crate) fn get_content_sha256(headers: &HeaderMap<HeaderValue>) -> Option<String> {
|
||||||
match get_request_auth_type(headers) {
|
get_content_sha256_with_query(headers, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_content_sha256_with_query(headers: &HeaderMap<HeaderValue>, query: Option<&str>) -> Option<String> {
|
||||||
|
match get_request_auth_type_with_query(headers, query) {
|
||||||
AuthType::Presigned | AuthType::Signed => {
|
AuthType::Presigned | AuthType::Signed => {
|
||||||
if skip_content_sha256_cksum(headers) {
|
if skip_content_sha256_cksum_with_query(headers, query) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(get_content_sha256_cksum(headers, ServiceType::S3))
|
Some(get_content_sha256_cksum_with_query(headers, query, ServiceType::S3))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -595,24 +601,21 @@ pub(crate) fn get_content_sha256(headers: &HeaderMap<HeaderValue>) -> Option<Str
|
|||||||
|
|
||||||
/// skip_content_sha256_cksum returns true if caller needs to skip
|
/// skip_content_sha256_cksum returns true if caller needs to skip
|
||||||
/// payload checksum, false if not.
|
/// payload checksum, false if not.
|
||||||
|
#[allow(dead_code)]
|
||||||
fn skip_content_sha256_cksum(headers: &HeaderMap<HeaderValue>) -> bool {
|
fn skip_content_sha256_cksum(headers: &HeaderMap<HeaderValue>) -> bool {
|
||||||
let content_sha256 = if is_request_presigned_signature_v4(headers) {
|
skip_content_sha256_cksum_with_query(headers, None)
|
||||||
// For presigned requests, check query params first, then headers
|
}
|
||||||
// Note: In a real implementation, you would need to check query parameters
|
|
||||||
// For now, we'll just check headers
|
|
||||||
headers.get(AMZ_CONTENT_SHA256)
|
|
||||||
} else {
|
|
||||||
headers.get(AMZ_CONTENT_SHA256)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip if no header was set
|
fn skip_content_sha256_cksum_with_query(headers: &HeaderMap<HeaderValue>, query: Option<&str>) -> bool {
|
||||||
|
let include_query_values = matches!(get_request_auth_type_with_query(headers, query), AuthType::Presigned);
|
||||||
|
let content_sha256 = get_content_sha256_value(headers, query, include_query_values);
|
||||||
|
|
||||||
|
// Skip if no checksum value was set in header/query for query-presigned requests.
|
||||||
let Some(header_value) = content_sha256 else {
|
let Some(header_value) = content_sha256 else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(value) = header_value.to_str() else {
|
let value = header_value;
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// If x-amz-content-sha256 is set and the value is not
|
// If x-amz-content-sha256 is set and the value is not
|
||||||
// 'UNSIGNED-PAYLOAD' we should validate the content sha256.
|
// 'UNSIGNED-PAYLOAD' we should validate the content sha256.
|
||||||
@@ -643,7 +646,11 @@ fn skip_content_sha256_cksum(headers: &HeaderMap<HeaderValue>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns SHA256 for calculating canonical-request.
|
/// Returns SHA256 for calculating canonical-request.
|
||||||
fn get_content_sha256_cksum(headers: &HeaderMap<HeaderValue>, service_type: ServiceType) -> String {
|
fn get_content_sha256_cksum_with_query(
|
||||||
|
headers: &HeaderMap<HeaderValue>,
|
||||||
|
query: Option<&str>,
|
||||||
|
service_type: ServiceType,
|
||||||
|
) -> String {
|
||||||
if service_type == ServiceType::STS {
|
if service_type == ServiceType::STS {
|
||||||
// For STS requests, we would need to read the body and calculate SHA256
|
// For STS requests, we would need to read the body and calculate SHA256
|
||||||
// This is a simplified implementation - in practice you'd need access to the request body
|
// This is a simplified implementation - in practice you'd need access to the request body
|
||||||
@@ -651,28 +658,54 @@ fn get_content_sha256_cksum(headers: &HeaderMap<HeaderValue>, service_type: Serv
|
|||||||
return "sts-body-sha256-placeholder".to_string();
|
return "sts-body-sha256-placeholder".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (default_sha256_cksum, content_sha256) = if is_request_presigned_signature_v4(headers) {
|
let (default_sha256_cksum, content_sha256) = if is_request_presigned_signature_v4_with_query(headers, query) {
|
||||||
// For a presigned request we look at the query param for sha256.
|
// For a presigned request we look at the query param for sha256.
|
||||||
// X-Amz-Content-Sha256, if not set in presigned requests, checksum
|
// X-Amz-Content-Sha256, if not set in presigned requests, checksum
|
||||||
// will default to 'UNSIGNED-PAYLOAD'.
|
// will default to 'UNSIGNED-PAYLOAD'.
|
||||||
(UNSIGNED_PAYLOAD.to_string(), headers.get(AMZ_CONTENT_SHA256))
|
(UNSIGNED_PAYLOAD.to_string(), get_content_sha256_value(headers, query, true))
|
||||||
} else {
|
} else {
|
||||||
// X-Amz-Content-Sha256, if not set in signed requests, checksum
|
// X-Amz-Content-Sha256, if not set in signed requests, checksum
|
||||||
// will default to sha256([]byte("")).
|
// will default to sha256([]byte("")).
|
||||||
(EMPTY_STRING_SHA256_HASH.to_string(), headers.get(AMZ_CONTENT_SHA256))
|
(
|
||||||
|
EMPTY_STRING_SHA256_HASH.to_string(),
|
||||||
|
headers
|
||||||
|
.get(AMZ_CONTENT_SHA256)
|
||||||
|
.and_then(|v| v.to_str().ok().map(str::to_owned)),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// We found 'X-Amz-Content-Sha256' return the captured value.
|
// We found 'X-Amz-Content-Sha256' return the captured value.
|
||||||
if let Some(header_value) = content_sha256
|
if let Some(header_value) = content_sha256 {
|
||||||
&& let Ok(value) = header_value.to_str()
|
return header_value;
|
||||||
{
|
|
||||||
return value.to_string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We couldn't find 'X-Amz-Content-Sha256'.
|
// We couldn't find 'X-Amz-Content-Sha256'.
|
||||||
default_sha256_cksum
|
default_sha256_cksum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_content_sha256_value(
|
||||||
|
headers: &HeaderMap<HeaderValue>,
|
||||||
|
query: Option<&str>,
|
||||||
|
include_query_for_presigned: bool,
|
||||||
|
) -> Option<String> {
|
||||||
|
if include_query_for_presigned && is_request_presigned_signature_v4_with_query(headers, query) {
|
||||||
|
return query
|
||||||
|
.and_then(|q| get_query_param(q, "x-amz-content-sha256"))
|
||||||
|
.or_else(|| headers.get(AMZ_CONTENT_SHA256).and_then(|v| v.to_str().ok()))
|
||||||
|
.map(str::to_owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers
|
||||||
|
.get(AMZ_CONTENT_SHA256)
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
.map(str::to_owned)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn get_content_sha256_cksum(headers: &HeaderMap<HeaderValue>, service_type: ServiceType) -> String {
|
||||||
|
get_content_sha256_cksum_with_query(headers, None, service_type)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user