Skip to content

Commit

Permalink
Fix SAS token generation for directory-scoped access (#1277)
Browse files Browse the repository at this point in the history
  • Loading branch information
David Weis committed May 15, 2023
1 parent 56c32bb commit dcf7738
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 additions & 0 deletions sdk/storage/src/shared_access_signature/service_sas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ pub struct BlobSharedAccessSignature {
identifier: Option<String>,
ip: Option<String>,
protocol: Option<SasProtocol>,
signed_directory_depth: Option<usize>, // sdd
}

impl BlobSharedAccessSignature {
Expand All @@ -120,6 +121,7 @@ impl BlobSharedAccessSignature {
identifier: None,
ip: None,
protocol: None,
signed_directory_depth: None,
}
}

Expand All @@ -128,6 +130,7 @@ impl BlobSharedAccessSignature {
identifier: String => Some(identifier),
ip: String => Some(ip),
protocol: SasProtocol => Some(protocol),
signed_directory_depth: usize => Some(signed_directory_depth),
}

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

if let Some(signed_directory_depth) = &self.signed_directory_depth {
elements.push(format!("sdd={signed_directory_depth}"))
}

let sig = self.sign();
elements.push(format!("sig={}", format_form(sig)));

elements.join("&")
}
}

#[cfg(test)]
mod test {
use super::*;
use std::collections::HashSet;
use time::Duration;

const MOCK_SECRET_KEY: &str = "RZfi3m1W7eyQ5zD4ymSmGANVdJ2SDQmg4sE89SW104s=";
const MOCK_CANONICALIZED_RESOURCE: &str = "/blob/STORAGE_ACCOUNT_NAME/CONTAINER_NAME/";

#[test]
fn test_blob_scoped_sas_token() {
let permissions = BlobSasPermissions {
read: true,
..Default::default()
};
let signed_token = BlobSharedAccessSignature::new(
String::from(MOCK_SECRET_KEY),
String::from(MOCK_CANONICALIZED_RESOURCE),
permissions,
OffsetDateTime::UNIX_EPOCH + Duration::days(7),
BlobSignedResource::Blob,
)
.token();

// splitting by & is only safe if & is not part of any fields
// if that changes in the future we might want to use a proper query string parser
let elements = signed_token.split('&').collect::<HashSet<_>>();

// BlobSignedResource::Blob
assert!(elements.contains("sr=b"));
// signed_directory_depth NOT set
assert!(!elements.iter().any(|element| element.starts_with("sdd=")));

assert_eq!(signed_token, "sv=2020-06-12&sp=r&sr=b&se=1970-01-08T00%3A00%3A00Z&sig=alEGfKjtiLs5LO%2FyrfPkzjQBHbk4Uda9XOezbRyKwEM%3D");
}

#[test]
fn test_directory_scoped_sas_token() {
let permissions = BlobSasPermissions {
read: true,
..Default::default()
};
let signed_token = BlobSharedAccessSignature::new(
String::from(MOCK_SECRET_KEY),
String::from(MOCK_CANONICALIZED_RESOURCE),
permissions,
OffsetDateTime::UNIX_EPOCH + Duration::days(7),
BlobSignedResource::Directory,
)
.signed_directory_depth(2_usize)
.token();

// splitting by & is only safe if & is not part of any fields
// if that changes in the future we might want to use a proper query string parser
let elements = signed_token.split('&').collect::<HashSet<_>>();

// BlobSignedResource::Blob
assert!(elements.contains("sr=d"));
// signed_directory_depth = 2
assert!(elements.contains("sdd=2"));

assert_eq!(signed_token, "sv=2020-06-12&sp=r&sr=d&se=1970-01-08T00%3A00%3A00Z&sdd=2&sig=e0eoY169%2Bex4AnI9ZAOiOaX49snoJiuvyJ22XV6qW2k%3D");
}
}

0 comments on commit dcf7738

Please sign in to comment.