Loading crates/s3s/src/ops/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ fn extract_host(req: &Request) -> S3Result<Option<String>> { fn extract_s3_path(host: Option<&str>, uri_path: &str, base_domain: Option<&str>) -> S3Result<S3Path> { let result = match (base_domain, host) { (Some(base_domain), Some(host)) => { (Some(base_domain), Some(host)) if base_domain != host => { debug!(?base_domain, ?host, ?uri_path, "parsing virtual-hosted-style request"); crate::path::parse_virtual_hosted_style(base_domain, host, uri_path) } Loading Loading @@ -230,6 +230,7 @@ async fn prepare(req: &mut Request, auth: Option<&dyn S3Auth>, base_domain: Opti hs, decoded_uri_path, s3_path, host: host.as_deref(), content_length, Loading crates/s3s/src/ops/signature.rs +27 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ use crate::error::*; use crate::http; use crate::http::{AwsChunkedStream, Body, Multipart}; use crate::http::{OrderedHeaders, OrderedQs}; use crate::path::S3Path; use crate::sig_v2; use crate::sig_v2::{AuthorizationV2, PresignedUrlV2}; use crate::sig_v4; Loading Loading @@ -63,6 +64,7 @@ pub struct SignatureContext<'a> { pub hs: OrderedHeaders<'a>, pub decoded_uri_path: String, pub s3_path: &'a S3Path, pub host: Option<&'a str>, pub content_length: Option<u64>, Loading Loading @@ -342,17 +344,29 @@ impl SignatureContext<'_> { None } fn v2_virtual_hosted_bucket(&self) -> Option<&str> { match (self.base_domain, self.host) { (Some(base_domain), Some(host)) if base_domain != host => self.s3_path.get_bucket_name(), _ => None, } } pub async fn v2_check_header_auth(&mut self, auth_v2: AuthorizationV2<'_>) -> S3Result<Credentials> { let method = &self.req_method; let uri_s3_path = super::extract_s3_path(self.host, self.req_uri.path(), self.base_domain)?; let date = sig_v2::get_date(&self.hs).ok_or_else(|| invalid_request!("missing date"))?; let date = self.hs.get_unique("date").or_else(|| self.hs.get_unique("x-amz-date")); if date.is_none() { return Err(invalid_request!("missing date")); } let auth = require_auth(self.auth)?; let access_key = auth_v2.access_key; let secret_key = auth.get_secret_key(access_key).await?; let string_to_sign = sig_v2::create_string_to_sign(method, date, &self.hs, &uri_s3_path, self.qs); let vh_bucket = self.v2_virtual_hosted_bucket(); let string_to_sign = sig_v2::create_string_to_sign(sig_v2::Mode::HeaderAuth, method, self.req_uri.path(), self.qs, &self.hs, vh_bucket); let signature = sig_v2::calculate_signature(&secret_key, &string_to_sign); debug!(?string_to_sign, "sig_v2 header_auth"); Loading @@ -373,8 +387,6 @@ impl SignatureContext<'_> { let qs = self.qs.unwrap(); // assume: qs has "Signature" let presigned_url = PresignedUrlV2::parse(qs).map_err(|err| invalid_request!(err, "missing presigned url v2 fields"))?; let uri_s3_path = super::extract_s3_path(self.host, self.req_uri.path(), self.base_domain)?; if time::OffsetDateTime::now_utc() > presigned_url.expires_time { return Err(s3_error!(AccessDenied, "Request has expired")); } Loading @@ -383,8 +395,16 @@ impl SignatureContext<'_> { let access_key = presigned_url.access_key; let secret_key = auth.get_secret_key(access_key).await?; let string_to_sign = sig_v2::create_string_to_sign(self.req_method, presigned_url.expires_str, &self.hs, &uri_s3_path, self.qs); let vh_bucket = self.v2_virtual_hosted_bucket(); let string_to_sign = sig_v2::create_string_to_sign( sig_v2::Mode::PresignedUrl, self.req_method, self.req_uri.path(), self.qs, &self.hs, vh_bucket, ); let signature = sig_v2::calculate_signature(&secret_key, &string_to_sign); let expected_signature = presigned_url.signature; Loading crates/s3s/src/path.rs +18 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,24 @@ impl S3Path { _ => None, } } /// Returns the bucket name part if the path is bucket or object #[must_use] pub fn get_bucket_name(&self) -> Option<&str> { match self { Self::Root => None, Self::Bucket { bucket } | Self::Object { bucket, .. } => Some(bucket), } } /// Returns the object key part if the path is object #[must_use] pub fn get_object_key(&self) -> Option<&str> { match self { Self::Root | Self::Bucket { .. } => None, Self::Object { key, .. } => Some(key), } } } /// See [bucket nameing rules](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) Loading crates/s3s/src/sig_v2/methods.rs +71 −60 Original line number Diff line number Diff line use crate::auth::SecretKey; use crate::http::OrderedHeaders; use crate::http::OrderedQs; use crate::path::S3Path; use crate::utils::crypto::hmac_sha1; use std::ops::Not; Loading Loading @@ -40,16 +39,19 @@ const INCLUDED_QUERY: &[&str] = &[ "website", ]; pub fn get_date<'a>(headers: &'_ OrderedHeaders<'a>) -> Option<&'a str> { headers.get_unique("x-amz-date").or_else(|| headers.get_unique("date")) #[derive(Debug, Clone, Copy)] pub enum Mode { HeaderAuth, PresignedUrl, } pub fn create_string_to_sign( mode: Mode, method: &Method, date_or_expires: &str, headers: &OrderedHeaders<'_>, uri_s3_path: &S3Path, uri_path: &str, qs: Option<&OrderedQs>, headers: &OrderedHeaders<'_>, virual_host_bucket: Option<&str>, ) -> String { let mut ans = String::with_capacity(256); Loading @@ -75,11 +77,25 @@ pub fn create_string_to_sign( ans.push('\n'); } { // {Date}\n or {Expires}\n ans.push_str(date_or_expires); match mode { // {Date}\n Mode::HeaderAuth => { // "if you include the x-amz-date header, use the empty string // for the Date when constructing the StringToSign." let mut date = headers.get_unique("date").unwrap_or_default(); if headers.get_unique("x-amz-date").is_some() { date = ""; } ans.push_str(date); ans.push('\n'); } // {Expires}\n Mode::PresignedUrl => { let expires = qs.and_then(|qs| qs.get_unique("Expires")).unwrap_or_default(); ans.push_str(expires); ans.push('\n'); } } { // {CanonicalizedAmzHeaders} Loading @@ -88,9 +104,6 @@ pub fn create_string_to_sign( if name.starts_with("x-amz-").not() { continue; } if name == "x-amz-date" { continue; } if name == last { continue; } Loading @@ -115,23 +128,13 @@ pub fn create_string_to_sign( { // {CanonicalizedResource} match uri_s3_path { S3Path::Root => { ans.push('/'); } S3Path::Bucket { bucket } => { if let Some(bucket) = virual_host_bucket { ans.push('/'); ans.push_str(bucket); ans.push('/'); } S3Path::Object { bucket, key } => { ans.push('/'); ans.push_str(bucket); ans.push('/'); ans.push_str(key); } } ans.push_str(uri_path); if let Some(qs) = qs { let mut is_first = true; for q in INCLUDED_QUERY { Loading Loading @@ -175,12 +178,12 @@ mod tests { { // Object GET let method = &Method::GET; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let date = "Tue, 27 Mar 2007 19:36:42 +0000"; let headers = OrderedHeaders::default(); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:36:42 +0000")]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -200,12 +203,15 @@ mod tests { { // Object PUT let method = &Method::PUT; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let date = "Tue, 27 Mar 2007 21:15:45 +0000"; let headers = OrderedHeaders::from_slice_unchecked(&[("content-type", "image/jpeg")]); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("content-type", "image/jpeg"), ("date", "Tue, 27 Mar 2007 21:15:45 +0000"), ]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -225,12 +231,12 @@ mod tests { { // List let method = &Method::GET; let s3_path = S3Path::bucket("awsexamplebucket1"); let date = "Tue, 27 Mar 2007 19:42:41 +0000"; let headers = OrderedHeaders::default(); let uri_path = "/"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:42:41 +0000")]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -250,12 +256,12 @@ mod tests { { // Fetch let method = &Method::GET; let s3_path = S3Path::bucket("awsexamplebucket1"); let date = "Tue, 27 Mar 2007 19:44:46 +0000"; let uri_path = "/"; let qs = OrderedQs::from_vec_unchecked(vec![("acl".into(), String::new())]); let headers = OrderedHeaders::default(); let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:44:46 +0000")]); let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, Some(&qs)); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, Some(&qs), &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -275,15 +281,15 @@ mod tests { { // Delete let method = &Method::DELETE; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let uri_path = "/awsexamplebucket1/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("date", "Tue, 27 Mar 2007 21:20:27 +0000"), ("x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"), ]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -292,18 +298,21 @@ mod tests { "DELETE\n", "\n", "\n", "Tue, 27 Mar 2007 21:20:26 +0000\n", "\n", "x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n", "/awsexamplebucket1/photos/puppy.jpg", ) ); assert_eq!(signature, "XbyTlbQdu9Xw5o8P4iMwPktxQd8="); // FIXME: The example is wrong? // assert_eq!(signature, "XbyTlbQdu9Xw5o8P4iMwPktxQd8="); assert_eq!(signature, "Ri1hpB1zpS9pGqR7y8kuNFCl4sE="); } { // Upload let method = &Method::PUT; let s3_path = S3Path::object("static.example.com", "db-backup.dat.gz"); let uri_path = "/db-backup.dat.gz"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("date", "Tue, 27 Mar 2007 21:06:08 +0000"), ("x-amz-acl", "public-read"), Loading @@ -318,9 +327,9 @@ mod tests { ("content-length", "5913339"), ]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = Some("static.example.com"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -345,12 +354,12 @@ mod tests { { // List all my buckets let method = &Method::GET; let s3_path = S3Path::Root; let uri_path = "/"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Wed, 28 Mar 2007 01:29:59 +0000")]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -370,12 +379,12 @@ mod tests { { // Unicode keys let method = &Method::GET; let uri_s3_path = crate::path::parse_path_style("/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re").unwrap(); let uri_path = "/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Wed, 28 Mar 2007 01:49:49 +0000")]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &uri_s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -395,7 +404,7 @@ mod tests { { // Query string request authentication let method = &Method::GET; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::default(); let qs = OrderedQs::parse(concat!( "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE", Loading @@ -404,10 +413,12 @@ mod tests { "&Expires=1175139620", )) .unwrap(); let vh_bucket = Some("awsexamplebucket1"); let presigned_url = super::super::PresignedUrlV2::parse(&qs).unwrap(); assert_eq!(presigned_url.access_key, access_key); let string_to_sign = create_string_to_sign(method, presigned_url.expires_str, &headers, &s3_path, Some(&qs)); let string_to_sign = create_string_to_sign(Mode::PresignedUrl, method, uri_path, Some(&qs), &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading crates/s3s/src/sig_v2/presigned_url_v2.rs +0 −2 Original line number Diff line number Diff line Loading @@ -7,7 +7,6 @@ use time::OffsetDateTime; pub struct PresignedUrlV2<'a> { pub access_key: &'a str, pub expires_time: OffsetDateTime, pub expires_str: &'a str, pub signature: Cow<'a, str>, } Loading @@ -33,7 +32,6 @@ impl<'a> PresignedUrlV2<'a> { Ok(Self { access_key, expires_time, expires_str, signature, }) } Loading Loading
crates/s3s/src/ops/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ fn extract_host(req: &Request) -> S3Result<Option<String>> { fn extract_s3_path(host: Option<&str>, uri_path: &str, base_domain: Option<&str>) -> S3Result<S3Path> { let result = match (base_domain, host) { (Some(base_domain), Some(host)) => { (Some(base_domain), Some(host)) if base_domain != host => { debug!(?base_domain, ?host, ?uri_path, "parsing virtual-hosted-style request"); crate::path::parse_virtual_hosted_style(base_domain, host, uri_path) } Loading Loading @@ -230,6 +230,7 @@ async fn prepare(req: &mut Request, auth: Option<&dyn S3Auth>, base_domain: Opti hs, decoded_uri_path, s3_path, host: host.as_deref(), content_length, Loading
crates/s3s/src/ops/signature.rs +27 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ use crate::error::*; use crate::http; use crate::http::{AwsChunkedStream, Body, Multipart}; use crate::http::{OrderedHeaders, OrderedQs}; use crate::path::S3Path; use crate::sig_v2; use crate::sig_v2::{AuthorizationV2, PresignedUrlV2}; use crate::sig_v4; Loading Loading @@ -63,6 +64,7 @@ pub struct SignatureContext<'a> { pub hs: OrderedHeaders<'a>, pub decoded_uri_path: String, pub s3_path: &'a S3Path, pub host: Option<&'a str>, pub content_length: Option<u64>, Loading Loading @@ -342,17 +344,29 @@ impl SignatureContext<'_> { None } fn v2_virtual_hosted_bucket(&self) -> Option<&str> { match (self.base_domain, self.host) { (Some(base_domain), Some(host)) if base_domain != host => self.s3_path.get_bucket_name(), _ => None, } } pub async fn v2_check_header_auth(&mut self, auth_v2: AuthorizationV2<'_>) -> S3Result<Credentials> { let method = &self.req_method; let uri_s3_path = super::extract_s3_path(self.host, self.req_uri.path(), self.base_domain)?; let date = sig_v2::get_date(&self.hs).ok_or_else(|| invalid_request!("missing date"))?; let date = self.hs.get_unique("date").or_else(|| self.hs.get_unique("x-amz-date")); if date.is_none() { return Err(invalid_request!("missing date")); } let auth = require_auth(self.auth)?; let access_key = auth_v2.access_key; let secret_key = auth.get_secret_key(access_key).await?; let string_to_sign = sig_v2::create_string_to_sign(method, date, &self.hs, &uri_s3_path, self.qs); let vh_bucket = self.v2_virtual_hosted_bucket(); let string_to_sign = sig_v2::create_string_to_sign(sig_v2::Mode::HeaderAuth, method, self.req_uri.path(), self.qs, &self.hs, vh_bucket); let signature = sig_v2::calculate_signature(&secret_key, &string_to_sign); debug!(?string_to_sign, "sig_v2 header_auth"); Loading @@ -373,8 +387,6 @@ impl SignatureContext<'_> { let qs = self.qs.unwrap(); // assume: qs has "Signature" let presigned_url = PresignedUrlV2::parse(qs).map_err(|err| invalid_request!(err, "missing presigned url v2 fields"))?; let uri_s3_path = super::extract_s3_path(self.host, self.req_uri.path(), self.base_domain)?; if time::OffsetDateTime::now_utc() > presigned_url.expires_time { return Err(s3_error!(AccessDenied, "Request has expired")); } Loading @@ -383,8 +395,16 @@ impl SignatureContext<'_> { let access_key = presigned_url.access_key; let secret_key = auth.get_secret_key(access_key).await?; let string_to_sign = sig_v2::create_string_to_sign(self.req_method, presigned_url.expires_str, &self.hs, &uri_s3_path, self.qs); let vh_bucket = self.v2_virtual_hosted_bucket(); let string_to_sign = sig_v2::create_string_to_sign( sig_v2::Mode::PresignedUrl, self.req_method, self.req_uri.path(), self.qs, &self.hs, vh_bucket, ); let signature = sig_v2::calculate_signature(&secret_key, &string_to_sign); let expected_signature = presigned_url.signature; Loading
crates/s3s/src/path.rs +18 −0 Original line number Diff line number Diff line Loading @@ -87,6 +87,24 @@ impl S3Path { _ => None, } } /// Returns the bucket name part if the path is bucket or object #[must_use] pub fn get_bucket_name(&self) -> Option<&str> { match self { Self::Root => None, Self::Bucket { bucket } | Self::Object { bucket, .. } => Some(bucket), } } /// Returns the object key part if the path is object #[must_use] pub fn get_object_key(&self) -> Option<&str> { match self { Self::Root | Self::Bucket { .. } => None, Self::Object { key, .. } => Some(key), } } } /// See [bucket nameing rules](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) Loading
crates/s3s/src/sig_v2/methods.rs +71 −60 Original line number Diff line number Diff line use crate::auth::SecretKey; use crate::http::OrderedHeaders; use crate::http::OrderedQs; use crate::path::S3Path; use crate::utils::crypto::hmac_sha1; use std::ops::Not; Loading Loading @@ -40,16 +39,19 @@ const INCLUDED_QUERY: &[&str] = &[ "website", ]; pub fn get_date<'a>(headers: &'_ OrderedHeaders<'a>) -> Option<&'a str> { headers.get_unique("x-amz-date").or_else(|| headers.get_unique("date")) #[derive(Debug, Clone, Copy)] pub enum Mode { HeaderAuth, PresignedUrl, } pub fn create_string_to_sign( mode: Mode, method: &Method, date_or_expires: &str, headers: &OrderedHeaders<'_>, uri_s3_path: &S3Path, uri_path: &str, qs: Option<&OrderedQs>, headers: &OrderedHeaders<'_>, virual_host_bucket: Option<&str>, ) -> String { let mut ans = String::with_capacity(256); Loading @@ -75,11 +77,25 @@ pub fn create_string_to_sign( ans.push('\n'); } { // {Date}\n or {Expires}\n ans.push_str(date_or_expires); match mode { // {Date}\n Mode::HeaderAuth => { // "if you include the x-amz-date header, use the empty string // for the Date when constructing the StringToSign." let mut date = headers.get_unique("date").unwrap_or_default(); if headers.get_unique("x-amz-date").is_some() { date = ""; } ans.push_str(date); ans.push('\n'); } // {Expires}\n Mode::PresignedUrl => { let expires = qs.and_then(|qs| qs.get_unique("Expires")).unwrap_or_default(); ans.push_str(expires); ans.push('\n'); } } { // {CanonicalizedAmzHeaders} Loading @@ -88,9 +104,6 @@ pub fn create_string_to_sign( if name.starts_with("x-amz-").not() { continue; } if name == "x-amz-date" { continue; } if name == last { continue; } Loading @@ -115,23 +128,13 @@ pub fn create_string_to_sign( { // {CanonicalizedResource} match uri_s3_path { S3Path::Root => { ans.push('/'); } S3Path::Bucket { bucket } => { if let Some(bucket) = virual_host_bucket { ans.push('/'); ans.push_str(bucket); ans.push('/'); } S3Path::Object { bucket, key } => { ans.push('/'); ans.push_str(bucket); ans.push('/'); ans.push_str(key); } } ans.push_str(uri_path); if let Some(qs) = qs { let mut is_first = true; for q in INCLUDED_QUERY { Loading Loading @@ -175,12 +178,12 @@ mod tests { { // Object GET let method = &Method::GET; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let date = "Tue, 27 Mar 2007 19:36:42 +0000"; let headers = OrderedHeaders::default(); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:36:42 +0000")]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -200,12 +203,15 @@ mod tests { { // Object PUT let method = &Method::PUT; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let date = "Tue, 27 Mar 2007 21:15:45 +0000"; let headers = OrderedHeaders::from_slice_unchecked(&[("content-type", "image/jpeg")]); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("content-type", "image/jpeg"), ("date", "Tue, 27 Mar 2007 21:15:45 +0000"), ]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -225,12 +231,12 @@ mod tests { { // List let method = &Method::GET; let s3_path = S3Path::bucket("awsexamplebucket1"); let date = "Tue, 27 Mar 2007 19:42:41 +0000"; let headers = OrderedHeaders::default(); let uri_path = "/"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:42:41 +0000")]); let qs = None; let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -250,12 +256,12 @@ mod tests { { // Fetch let method = &Method::GET; let s3_path = S3Path::bucket("awsexamplebucket1"); let date = "Tue, 27 Mar 2007 19:44:46 +0000"; let uri_path = "/"; let qs = OrderedQs::from_vec_unchecked(vec![("acl".into(), String::new())]); let headers = OrderedHeaders::default(); let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Tue, 27 Mar 2007 19:44:46 +0000")]); let vh_bucket = Some("awsexamplebucket1"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, Some(&qs)); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, Some(&qs), &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -275,15 +281,15 @@ mod tests { { // Delete let method = &Method::DELETE; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let uri_path = "/awsexamplebucket1/photos/puppy.jpg"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("date", "Tue, 27 Mar 2007 21:20:27 +0000"), ("x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"), ]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -292,18 +298,21 @@ mod tests { "DELETE\n", "\n", "\n", "Tue, 27 Mar 2007 21:20:26 +0000\n", "\n", "x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n", "/awsexamplebucket1/photos/puppy.jpg", ) ); assert_eq!(signature, "XbyTlbQdu9Xw5o8P4iMwPktxQd8="); // FIXME: The example is wrong? // assert_eq!(signature, "XbyTlbQdu9Xw5o8P4iMwPktxQd8="); assert_eq!(signature, "Ri1hpB1zpS9pGqR7y8kuNFCl4sE="); } { // Upload let method = &Method::PUT; let s3_path = S3Path::object("static.example.com", "db-backup.dat.gz"); let uri_path = "/db-backup.dat.gz"; let headers = OrderedHeaders::from_slice_unchecked(&[ ("date", "Tue, 27 Mar 2007 21:06:08 +0000"), ("x-amz-acl", "public-read"), Loading @@ -318,9 +327,9 @@ mod tests { ("content-length", "5913339"), ]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = Some("static.example.com"); let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -345,12 +354,12 @@ mod tests { { // List all my buckets let method = &Method::GET; let s3_path = S3Path::Root; let uri_path = "/"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Wed, 28 Mar 2007 01:29:59 +0000")]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -370,12 +379,12 @@ mod tests { { // Unicode keys let method = &Method::GET; let uri_s3_path = crate::path::parse_path_style("/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re").unwrap(); let uri_path = "/dictionary/fran%C3%A7ais/pr%c3%a9f%c3%a8re"; let headers = OrderedHeaders::from_slice_unchecked(&[("date", "Wed, 28 Mar 2007 01:49:49 +0000")]); let qs = None; let date = get_date(&headers).unwrap(); let vh_bucket = None; let string_to_sign = create_string_to_sign(method, date, &headers, &uri_s3_path, qs); let string_to_sign = create_string_to_sign(Mode::HeaderAuth, method, uri_path, qs, &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading @@ -395,7 +404,7 @@ mod tests { { // Query string request authentication let method = &Method::GET; let s3_path = S3Path::object("awsexamplebucket1", "photos/puppy.jpg"); let uri_path = "/photos/puppy.jpg"; let headers = OrderedHeaders::default(); let qs = OrderedQs::parse(concat!( "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE", Loading @@ -404,10 +413,12 @@ mod tests { "&Expires=1175139620", )) .unwrap(); let vh_bucket = Some("awsexamplebucket1"); let presigned_url = super::super::PresignedUrlV2::parse(&qs).unwrap(); assert_eq!(presigned_url.access_key, access_key); let string_to_sign = create_string_to_sign(method, presigned_url.expires_str, &headers, &s3_path, Some(&qs)); let string_to_sign = create_string_to_sign(Mode::PresignedUrl, method, uri_path, Some(&qs), &headers, vh_bucket); let signature = calculate_signature(&secret_key, &string_to_sign); assert_eq!( Loading
crates/s3s/src/sig_v2/presigned_url_v2.rs +0 −2 Original line number Diff line number Diff line Loading @@ -7,7 +7,6 @@ use time::OffsetDateTime; pub struct PresignedUrlV2<'a> { pub access_key: &'a str, pub expires_time: OffsetDateTime, pub expires_str: &'a str, pub signature: Cow<'a, str>, } Loading @@ -33,7 +32,6 @@ impl<'a> PresignedUrlV2<'a> { Ok(Self { access_key, expires_time, expires_str, signature, }) } Loading