azure_storage/shared_access_signature/
account_sas.rsuse crate::shared_access_signature::{format_date, SasProtocol, SasToken};
use azure_core::{auth::Secret, hmac::hmac_sha256};
use std::fmt;
use time::OffsetDateTime;
use url::form_urlencoded;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AccountSasVersion {
V20181109,
V20150405,
V20130815,
V20120212,
}
impl fmt::Display for AccountSasVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::V20181109 => write!(f, "2018-11-09"),
Self::V20150405 => write!(f, "2015-04-05"),
Self::V20130815 => write!(f, "2013-08-15"),
Self::V20120212 => write!(f, "2012-02-12"),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AccountSasService {
Blob,
Queue,
Table,
File,
}
impl fmt::Display for AccountSasService {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Blob => write!(f, "b"),
Self::Queue => write!(f, "q"),
Self::Table => write!(f, "t"),
Self::File => write!(f, "f"),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AccountSasResource {
Blob,
Queue,
Table,
File,
}
impl fmt::Display for AccountSasResource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Blob => write!(f, "b"),
Self::Queue => write!(f, "q"),
Self::Table => write!(f, "t"),
Self::File => write!(f, "f"),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum AccountSasResourceType {
Service,
Container,
Object,
}
impl fmt::Display for AccountSasResourceType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::Service => write!(f, "s"),
Self::Container => write!(f, "c"),
Self::Object => write!(f, "o"),
}
}
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
pub struct AccountSasPermissions {
pub read: bool,
pub write: bool,
pub delete: bool,
pub list: bool,
pub add: bool,
pub create: bool,
pub update: bool,
pub process: bool,
}
impl fmt::Display for AccountSasPermissions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.read {
write!(f, "r")?;
}
if self.add {
write!(f, "a")?;
}
if self.create {
write!(f, "c")?;
}
if self.write {
write!(f, "w")?;
}
if self.delete {
write!(f, "d")?;
}
if self.list {
write!(f, "l")?;
}
if self.update {
write!(f, "u")?;
}
if self.process {
write!(f, "p")?;
}
Ok(())
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct AccountSharedAccessSignature {
account: String,
key: Secret,
version: AccountSasVersion,
resource: AccountSasResource,
resource_type: AccountSasResourceType,
expiry: OffsetDateTime,
permissions: AccountSasPermissions,
start: Option<OffsetDateTime>,
ip: Option<String>,
protocol: Option<SasProtocol>,
}
impl AccountSharedAccessSignature {
pub fn new(
account: String,
key: Secret,
resource: AccountSasResource,
resource_type: AccountSasResourceType,
expiry: OffsetDateTime,
permissions: AccountSasPermissions,
) -> Self {
Self {
account,
key,
version: AccountSasVersion::V20181109,
resource,
resource_type,
expiry,
permissions,
start: None,
ip: None,
protocol: None,
}
}
setters! {
version: AccountSasVersion => version,
start: OffsetDateTime => Some(start),
ip: String => Some(ip),
protocol: SasProtocol => Some(protocol),
}
fn sign(&self) -> azure_core::Result<String> {
match self.version {
AccountSasVersion::V20181109 => {
let string_to_sign = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
self.account,
self.permissions,
self.resource,
self.resource_type,
self.start.map_or(String::new(), format_date),
format_date(self.expiry),
self.ip.clone().unwrap_or_default(),
self.protocol
.as_ref()
.map_or(String::new(), ToString::to_string),
self.version,
);
hmac_sha256(&string_to_sign, &self.key)
}
_ => {
unimplemented!("Versions older than 2018-11-09 not supported");
}
}
}
}
impl SasToken for AccountSharedAccessSignature {
fn token(&self) -> azure_core::Result<String> {
let mut form = form_urlencoded::Serializer::new(String::new());
form.extend_pairs(&[
("sv", &self.version.to_string()),
("ss", &self.resource.to_string()),
("srt", &self.resource_type.to_string()),
("se", &format_date(self.expiry)),
("sp", &self.permissions.to_string()),
]);
if let Some(start) = &self.start {
form.append_pair("st", &format_date(*start));
}
if let Some(ip) = &self.ip {
form.append_pair("sip", ip);
}
if let Some(protocol) = &self.protocol {
form.append_pair("spr", &protocol.to_string());
}
let sig = self.sign()?;
form.append_pair("sig", &sig);
Ok(form.finish())
}
}