Skip to content

Commit dcf7738

Browse files
author
David Weis
authored
Fix SAS token generation for directory-scoped access (#1277)
1 parent 56c32bb commit dcf7738

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

sdk/storage/src/shared_access_signature/service_sas.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub struct BlobSharedAccessSignature {
100100
identifier: Option<String>,
101101
ip: Option<String>,
102102
protocol: Option<SasProtocol>,
103+
signed_directory_depth: Option<usize>, // sdd
103104
}
104105

105106
impl BlobSharedAccessSignature {
@@ -120,6 +121,7 @@ impl BlobSharedAccessSignature {
120121
identifier: None,
121122
ip: None,
122123
protocol: None,
124+
signed_directory_depth: None,
123125
}
124126
}
125127

@@ -128,6 +130,7 @@ impl BlobSharedAccessSignature {
128130
identifier: String => Some(identifier),
129131
ip: String => Some(ip),
130132
protocol: SasProtocol => Some(protocol),
133+
signed_directory_depth: usize => Some(signed_directory_depth),
131134
}
132135

133136
fn sign(&self) -> String {
@@ -179,9 +182,78 @@ impl SasToken for BlobSharedAccessSignature {
179182
elements.push(format!("spr={protocol}"))
180183
}
181184

185+
if let Some(signed_directory_depth) = &self.signed_directory_depth {
186+
elements.push(format!("sdd={signed_directory_depth}"))
187+
}
188+
182189
let sig = self.sign();
183190
elements.push(format!("sig={}", format_form(sig)));
184191

185192
elements.join("&")
186193
}
187194
}
195+
196+
#[cfg(test)]
197+
mod test {
198+
use super::*;
199+
use std::collections::HashSet;
200+
use time::Duration;
201+
202+
const MOCK_SECRET_KEY: &str = "RZfi3m1W7eyQ5zD4ymSmGANVdJ2SDQmg4sE89SW104s=";
203+
const MOCK_CANONICALIZED_RESOURCE: &str = "/blob/STORAGE_ACCOUNT_NAME/CONTAINER_NAME/";
204+
205+
#[test]
206+
fn test_blob_scoped_sas_token() {
207+
let permissions = BlobSasPermissions {
208+
read: true,
209+
..Default::default()
210+
};
211+
let signed_token = BlobSharedAccessSignature::new(
212+
String::from(MOCK_SECRET_KEY),
213+
String::from(MOCK_CANONICALIZED_RESOURCE),
214+
permissions,
215+
OffsetDateTime::UNIX_EPOCH + Duration::days(7),
216+
BlobSignedResource::Blob,
217+
)
218+
.token();
219+
220+
// splitting by & is only safe if & is not part of any fields
221+
// if that changes in the future we might want to use a proper query string parser
222+
let elements = signed_token.split('&').collect::<HashSet<_>>();
223+
224+
// BlobSignedResource::Blob
225+
assert!(elements.contains("sr=b"));
226+
// signed_directory_depth NOT set
227+
assert!(!elements.iter().any(|element| element.starts_with("sdd=")));
228+
229+
assert_eq!(signed_token, "sv=2020-06-12&sp=r&sr=b&se=1970-01-08T00%3A00%3A00Z&sig=alEGfKjtiLs5LO%2FyrfPkzjQBHbk4Uda9XOezbRyKwEM%3D");
230+
}
231+
232+
#[test]
233+
fn test_directory_scoped_sas_token() {
234+
let permissions = BlobSasPermissions {
235+
read: true,
236+
..Default::default()
237+
};
238+
let signed_token = BlobSharedAccessSignature::new(
239+
String::from(MOCK_SECRET_KEY),
240+
String::from(MOCK_CANONICALIZED_RESOURCE),
241+
permissions,
242+
OffsetDateTime::UNIX_EPOCH + Duration::days(7),
243+
BlobSignedResource::Directory,
244+
)
245+
.signed_directory_depth(2_usize)
246+
.token();
247+
248+
// splitting by & is only safe if & is not part of any fields
249+
// if that changes in the future we might want to use a proper query string parser
250+
let elements = signed_token.split('&').collect::<HashSet<_>>();
251+
252+
// BlobSignedResource::Blob
253+
assert!(elements.contains("sr=d"));
254+
// signed_directory_depth = 2
255+
assert!(elements.contains("sdd=2"));
256+
257+
assert_eq!(signed_token, "sv=2020-06-12&sp=r&sr=d&se=1970-01-08T00%3A00%3A00Z&sdd=2&sig=e0eoY169%2Bex4AnI9ZAOiOaX49snoJiuvyJ22XV6qW2k%3D");
258+
}
259+
}

0 commit comments

Comments
 (0)