#[cfg_attr(not(feature = "key_auth"), allow(unused_imports))]
use azure_core::{credentials::Secret, hmac::hmac_sha256, http::Method};
use crate::resource_context::ResourceLink;
#[cfg_attr(not(feature = "key_auth"), allow(dead_code))]
pub(crate) struct SignatureTarget<'a> {
http_method: Method,
link: &'a ResourceLink,
date_string: &'a str,
}
impl<'a> SignatureTarget<'a> {
pub fn new(http_method: Method, link: &'a ResourceLink, date_string: &'a str) -> Self {
SignatureTarget {
http_method,
link,
date_string,
}
}
#[cfg(feature = "key_auth")]
pub fn into_authorization(self, key: &Secret) -> azure_core::Result<String> {
let string_to_sign = self.into_signable_string();
tracing::debug!(signature_payload = ?string_to_sign, "generating Cosmos auth signature");
let signature = hmac_sha256(&string_to_sign, key)?;
Ok(format!("type=master&ver=1.0&sig={signature}"))
}
#[cfg(feature = "key_auth")]
fn into_signable_string(self) -> String {
format!(
"{}\n{}\n{}\n{}\n\n",
match self.http_method {
Method::Get => "get",
Method::Put => "put",
Method::Post => "post",
Method::Delete => "delete",
Method::Head => "head",
Method::Patch => "patch",
_ => "extension",
},
self.link.resource_type().path_segment(),
self.link.link_for_signing(),
self.date_string,
)
}
}
#[cfg(test)]
#[cfg(feature = "key_auth")]
mod tests {
use azure_core::{http::Method, time};
use crate::{
pipeline::signature_target::SignatureTarget,
resource_context::{ResourceLink, ResourceType},
};
#[test]
fn into_signable_string_generates_correct_value() {
let time_nonce = time::parse_rfc3339("1900-01-01T01:00:00.000000000+00:00").unwrap();
let date_string = time::to_rfc7231(&time_nonce).to_lowercase();
let ret = SignatureTarget::new(
Method::Get,
&ResourceLink::root(ResourceType::Databases)
.item("MyDatabase")
.feed(ResourceType::Containers)
.item("MyCollection"),
&date_string,
)
.into_signable_string();
assert_eq!(
ret,
"get
colls
dbs/MyDatabase/colls/MyCollection
mon, 01 jan 1900 01:00:00 gmt
"
);
}
#[test]
fn into_signable_string_does_not_url_encode_rid_links() {
let time_nonce = time::parse_rfc3339("1900-01-01T01:00:00.000000000+00:00").unwrap();
let date_string = time::to_rfc7231(&time_nonce).to_lowercase();
let ret = SignatureTarget::new(
Method::Get,
&ResourceLink::root(ResourceType::Databases)
.item_by_rid("ABCDEF==")
.feed(ResourceType::Containers)
.item_by_rid("XYZ123+="),
&date_string,
)
.into_signable_string();
assert_eq!(
ret,
"get
colls
dbs/ABCDEF==/colls/XYZ123+=
mon, 01 jan 1900 01:00:00 gmt
"
);
}
}