azure_storage/shared_access_signature/
account_sas.rs1use crate::shared_access_signature::{format_date, SasProtocol, SasToken};
2use azure_core::{auth::Secret, hmac::hmac_sha256};
3use std::fmt;
4use time::OffsetDateTime;
5use url::form_urlencoded;
6
7#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9pub enum AccountSasVersion {
10 V20181109,
11 V20150405,
12 V20130815,
13 V20120212,
14}
15
16impl fmt::Display for AccountSasVersion {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 match *self {
19 Self::V20181109 => write!(f, "2018-11-09"),
20 Self::V20150405 => write!(f, "2015-04-05"),
21 Self::V20130815 => write!(f, "2013-08-15"),
22 Self::V20120212 => write!(f, "2012-02-12"),
23 }
24 }
25}
26
27#[derive(Copy, Clone, PartialEq, Eq, Debug)]
28pub enum AccountSasService {
29 Blob,
30 Queue,
31 Table,
32 File,
33}
34
35impl fmt::Display for AccountSasService {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match *self {
38 Self::Blob => write!(f, "b"),
39 Self::Queue => write!(f, "q"),
40 Self::Table => write!(f, "t"),
41 Self::File => write!(f, "f"),
42 }
43 }
44}
45
46#[derive(Copy, Clone, PartialEq, Eq, Debug)]
48pub enum AccountSasResource {
49 Blob,
50 Queue,
51 Table,
52 File,
53}
54
55impl fmt::Display for AccountSasResource {
56 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57 match *self {
58 Self::Blob => write!(f, "b"),
59 Self::Queue => write!(f, "q"),
60 Self::Table => write!(f, "t"),
61 Self::File => write!(f, "f"),
62 }
63 }
64}
65
66#[derive(Copy, Clone, PartialEq, Eq, Debug)]
67pub enum AccountSasResourceType {
68 Service,
69 Container,
70 Object,
71}
72
73impl fmt::Display for AccountSasResourceType {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 match *self {
76 Self::Service => write!(f, "s"),
77 Self::Container => write!(f, "c"),
78 Self::Object => write!(f, "o"),
79 }
80 }
81}
82
83#[allow(clippy::struct_excessive_bools)]
85#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
86pub struct AccountSasPermissions {
87 pub read: bool,
88 pub write: bool,
89 pub delete: bool,
90 pub list: bool,
91 pub add: bool,
92 pub create: bool,
93 pub update: bool,
94 pub process: bool,
95}
96
97impl fmt::Display for AccountSasPermissions {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 if self.read {
103 write!(f, "r")?;
104 }
105 if self.add {
106 write!(f, "a")?;
107 }
108 if self.create {
109 write!(f, "c")?;
110 }
111 if self.write {
112 write!(f, "w")?;
113 }
114 if self.delete {
115 write!(f, "d")?;
116 }
117 if self.list {
118 write!(f, "l")?;
119 }
120 if self.update {
121 write!(f, "u")?;
122 }
123 if self.process {
124 write!(f, "p")?;
125 }
126
127 Ok(())
128 }
129}
130
131#[derive(PartialEq, Eq, Debug)]
132pub struct AccountSharedAccessSignature {
133 account: String,
134 key: Secret,
135 version: AccountSasVersion,
136 resource: AccountSasResource,
137 resource_type: AccountSasResourceType,
138 expiry: OffsetDateTime,
139 permissions: AccountSasPermissions,
140 start: Option<OffsetDateTime>,
141 ip: Option<String>,
142 protocol: Option<SasProtocol>,
143}
144
145impl AccountSharedAccessSignature {
146 pub fn new(
147 account: String,
148 key: Secret,
149 resource: AccountSasResource,
150 resource_type: AccountSasResourceType,
151 expiry: OffsetDateTime,
152 permissions: AccountSasPermissions,
153 ) -> Self {
154 Self {
155 account,
156 key,
157 version: AccountSasVersion::V20181109,
158 resource,
159 resource_type,
160 expiry,
161 permissions,
162 start: None,
163 ip: None,
164 protocol: None,
165 }
166 }
167
168 setters! {
169 version: AccountSasVersion => version,
170 start: OffsetDateTime => Some(start),
171 ip: String => Some(ip),
172 protocol: SasProtocol => Some(protocol),
173 }
174
175 fn sign(&self) -> azure_core::Result<String> {
177 match self.version {
178 AccountSasVersion::V20181109 => {
179 let string_to_sign = format!(
180 "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
181 self.account,
182 self.permissions,
183 self.resource,
184 self.resource_type,
185 self.start.map_or(String::new(), format_date),
186 format_date(self.expiry),
187 self.ip.clone().unwrap_or_default(),
188 self.protocol
189 .as_ref()
190 .map_or(String::new(), ToString::to_string),
191 self.version,
192 );
193
194 hmac_sha256(&string_to_sign, &self.key)
195 }
196 _ => {
197 unimplemented!("Versions older than 2018-11-09 not supported");
199 }
200 }
201 }
202}
203
204impl SasToken for AccountSharedAccessSignature {
205 fn token(&self) -> azure_core::Result<String> {
207 let mut form = form_urlencoded::Serializer::new(String::new());
208 form.extend_pairs(&[
209 ("sv", &self.version.to_string()),
210 ("ss", &self.resource.to_string()),
211 ("srt", &self.resource_type.to_string()),
212 ("se", &format_date(self.expiry)),
213 ("sp", &self.permissions.to_string()),
214 ]);
215
216 if let Some(start) = &self.start {
217 form.append_pair("st", &format_date(*start));
218 }
219 if let Some(ip) = &self.ip {
220 form.append_pair("sip", ip);
221 }
222 if let Some(protocol) = &self.protocol {
223 form.append_pair("spr", &protocol.to_string());
224 }
225 let sig = self.sign()?;
226 form.append_pair("sig", &sig);
227 Ok(form.finish())
228 }
229}