aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2024-02-27 19:13:24 +0100
committerAlex Auvolat <alex@adnab.me>2024-02-27 19:13:24 +0100
commit2aa049c781be87ae77e334996558fd82a2409506 (patch)
tree63843d4e4ff9b85461a2bb78756d163b49d148de
parent7271e69fd0dd98fca3b3ca2e499fb861594b066a (diff)
downloadgarage-2aa049c781be87ae77e334996558fd82a2409506.tar.gz
garage-2aa049c781be87ae77e334996558fd82a2409506.zip
[fix-presigned] fix tests
-rw-r--r--src/api/signature/payload.rs60
-rw-r--r--src/garage/tests/s3/streaming_signature.rs2
2 files changed, 28 insertions, 34 deletions
diff --git a/src/api/signature/payload.rs b/src/api/signature/payload.rs
index e2334ce7..29ed7081 100644
--- a/src/api/signature/payload.rs
+++ b/src/api/signature/payload.rs
@@ -1,9 +1,9 @@
use std::collections::HashMap;
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryFrom;
use chrono::{DateTime, Duration, NaiveDateTime, TimeZone, Utc};
use hmac::Mac;
-use hyper::header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, CONTENT_TYPE, HOST};
+use hyper::header::{HeaderMap, HeaderName, AUTHORIZATION, CONTENT_TYPE, HOST};
use hyper::{body::Incoming as IncomingBody, Method, Request};
use sha2::{Digest, Sha256};
@@ -29,6 +29,9 @@ pub const X_AMZ_CONTENT_SH256: HeaderName = HeaderName::from_static("x-amz-conte
pub const AWS4_HMAC_SHA256: &str = "AWS4-HMAC-SHA256";
pub const UNSIGNED_PAYLOAD: &str = "UNSIGNED-PAYLOAD";
+pub const STREAMING_AWS4_HMAC_SHA256_PAYLOAD: &str = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
+
+pub type QueryMap = HashMap<String, String>;
pub async fn check_payload_signature(
garage: &Garage,
@@ -37,7 +40,7 @@ pub async fn check_payload_signature(
) -> Result<(Option<Key>, Option<Hash>), Error> {
let query = parse_query_map(request.uri())?;
- let res = if query.contains_key(X_AMZ_ALGORITHM) {
+ let res = if query.contains_key(X_AMZ_ALGORITHM.as_str()) {
check_presigned_signature(garage, service, request, query).await
} else {
check_standard_signature(garage, service, request, query).await
@@ -54,7 +57,7 @@ async fn check_standard_signature(
garage: &Garage,
service: &'static str,
request: &Request<IncomingBody>,
- query: HeaderMap,
+ query: QueryMap,
) -> Result<(Option<Key>, Option<Hash>), Error> {
let authorization = Authorization::parse(request.headers())?;
@@ -96,7 +99,7 @@ async fn check_standard_signature(
let content_sha256 = if authorization.content_sha256 == UNSIGNED_PAYLOAD {
None
- } else if authorization.content_sha256 == "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" {
+ } else if authorization.content_sha256 == STREAMING_AWS4_HMAC_SHA256_PAYLOAD {
let bytes = hex::decode(authorization.signature).ok_or_bad_request("Invalid signature")?;
Some(Hash::try_from(&bytes).ok_or_bad_request("Invalid signature")?)
} else {
@@ -112,9 +115,9 @@ async fn check_presigned_signature(
garage: &Garage,
service: &'static str,
request: &Request<IncomingBody>,
- mut query: HeaderMap,
+ mut query: QueryMap,
) -> Result<(Option<Key>, Option<Hash>), Error> {
- let algorithm = query.get(X_AMZ_ALGORITHM).unwrap().to_str()?;
+ let algorithm = query.get(X_AMZ_ALGORITHM.as_str()).unwrap();
let authorization = Authorization::parse_presigned(algorithm, &query)?;
// Check that all mandatory signed headers are included
@@ -133,7 +136,7 @@ async fn check_presigned_signature(
return Err(Error::bad_request("Header `Host` should be signed"));
}
- query.remove(X_AMZ_SIGNATURE);
+ query.remove(X_AMZ_SIGNATURE.as_str());
let canonical_request = canonical_request(
service,
request.method(),
@@ -157,21 +160,15 @@ async fn check_presigned_signature(
Ok((Some(key), None))
}
-pub fn parse_query_map(uri: &http::uri::Uri) -> Result<HeaderMap, Error> {
- let mut query = HeaderMap::new();
+pub fn parse_query_map(uri: &http::uri::Uri) -> Result<QueryMap, Error> {
+ let mut query = QueryMap::new();
if let Some(query_str) = uri.query() {
let query_pairs = url::form_urlencoded::parse(query_str.as_bytes());
for (key, val) in query_pairs {
- let name: HeaderName = key
- .to_lowercase()
- .try_into()
- .ok_or_bad_request("invalid query parameter name")?;
- let value = HeaderValue::from_bytes(val.as_bytes())
- .ok_or_bad_request("invalid query parameter value")?;
- if query.insert(name.clone(), value).is_some() {
+ if query.insert(key.to_string(), val.into_owned()).is_some() {
return Err(Error::bad_request(format!(
"duplicate query parameter: `{}`",
- name
+ key
)));
}
}
@@ -205,7 +202,7 @@ pub fn canonical_request(
service: &'static str,
method: &Method,
canonical_uri: &str,
- query: &HeaderMap,
+ query: &QueryMap,
headers: &HeaderMap,
mut signed_headers: Vec<HeaderName>,
content_sha256: &str,
@@ -249,8 +246,7 @@ pub fn canonical_request(
let canonical_query_string = {
let mut items = Vec::with_capacity(query.len());
for (key, value) in query.iter() {
- let value = std::str::from_utf8(value.as_bytes())?;
- items.push(uri_encode(key.as_str(), true) + "=" + &uri_encode(value, true));
+ items.push(uri_encode(&key, true) + "=" + &uri_encode(&value, true));
}
items.sort();
items.join("&")
@@ -403,7 +399,7 @@ impl Authorization {
Ok(auth)
}
- fn parse_presigned(algorithm: &str, query: &HeaderMap) -> Result<Self, Error> {
+ fn parse_presigned(algorithm: &str, query: &QueryMap) -> Result<Self, Error> {
if algorithm != AWS4_HMAC_SHA256 {
return Err(Error::bad_request(
"Unsupported authorization method".to_string(),
@@ -411,19 +407,18 @@ impl Authorization {
}
let cred = query
- .get(X_AMZ_CREDENTIAL)
+ .get(X_AMZ_CREDENTIAL.as_str())
.ok_or_bad_request("X-Amz-Credential not found in query parameters")?;
let signed_headers = query
- .get(X_AMZ_SIGNEDHEADERS)
+ .get(X_AMZ_SIGNEDHEADERS.as_str())
.ok_or_bad_request("X-Amz-SignedHeaders not found in query parameters")?;
let signature = query
- .get(X_AMZ_SIGNATURE)
+ .get(X_AMZ_SIGNATURE.as_str())
.ok_or_bad_request("X-Amz-Signature not found in query parameters")?;
let duration = query
- .get(X_AMZ_EXPIRES)
+ .get(X_AMZ_EXPIRES.as_str())
.ok_or_bad_request("X-Amz-Expires not found in query parameters")?
- .to_str()?
.parse()
.map_err(|_| Error::bad_request("X-Amz-Expires is not a number".to_string()))?;
@@ -434,21 +429,20 @@ impl Authorization {
}
let date = query
- .get(X_AMZ_DATE)
- .ok_or_bad_request("Missing X-Amz-Date field")?
- .to_str()?;
+ .get(X_AMZ_DATE.as_str())
+ .ok_or_bad_request("Missing X-Amz-Date field")?;
let date = parse_date(date)?;
if Utc::now() - date > Duration::seconds(duration) {
return Err(Error::bad_request("Date is too old".to_string()));
}
- let (key_id, scope) = parse_credential(cred.to_str()?)?;
+ let (key_id, scope) = parse_credential(cred)?;
Ok(Authorization {
key_id,
scope,
- signed_headers: signed_headers.to_str()?.to_string(),
- signature: signature.to_str()?.to_string(),
+ signed_headers: signed_headers.to_string(),
+ signature: signature.to_string(),
content_sha256: UNSIGNED_PAYLOAD.to_string(),
date,
})
diff --git a/src/garage/tests/s3/streaming_signature.rs b/src/garage/tests/s3/streaming_signature.rs
index 224b9ed5..351aa422 100644
--- a/src/garage/tests/s3/streaming_signature.rs
+++ b/src/garage/tests/s3/streaming_signature.rs
@@ -26,7 +26,7 @@ async fn test_putobject_streaming() {
.builder(bucket.clone())
.method(Method::PUT)
.path(STD_KEY.to_owned())
- .unsigned_headers(headers)
+ .signed_headers(headers)
.vhost_style(true)
.body(vec![])
.body_signature(BodySignature::Streaming(10))