Skip to content

[Storage] SAS proof-of-concept #2798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions sdk/storage/azure_storage_blob/src/clients/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,7 @@ impl BlobClient {
})
}

pub fn new_no_credential(
endpoint: &str,
container_name: String,
blob_name: String,
options: Option<BlobClientOptions>,
) -> Result<Self> {
pub fn from_blob_url(blob_url: &str, options: Option<BlobClientOptions>) -> Result<Self> {
let mut options = options.unwrap_or_default();

let storage_headers_policy = Arc::new(StorageHeadersPolicy);
Expand All @@ -97,14 +92,35 @@ impl BlobClient {
.per_call_policies
.push(storage_headers_policy);

// TODO: We would abstract this out to a helper that is fancier and can handle edge-cases, which Url crate may not have been designed to handle
// Parse out container name and blob name, then remove them from the path
let mut container_name = "";
let mut blob_name = "";
let mut url = Url::parse(blob_url)?;
// URL BEFORE: https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig
// println!("URL BEFORE: {}", url.clone());

let url_for_path_segments = url.clone();
if let Some(mut segments) = url_for_path_segments.path_segments() {
container_name = segments.next().unwrap();
blob_name = segments.next().unwrap();
}
url.set_path("");
// URL AFTER NULLED PATH: https://vincenttranstock.blob.core.windows.net/?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig
// println!("URL AFTER NULLED PATH: {}", url.clone());

// Therefore, given the current design, the endpoint passed to the generated client will have query parameters on it.
let client = GeneratedBlobClient::new_no_credential(
endpoint,
container_name.clone(),
blob_name.clone(),
url.as_str(),
container_name.to_string(),
blob_name.to_string(),
Some(options),
)?;

// Consequently that means currently endpoint() will have all the query parameters in it.
// We could strip it out here before saving on this client, but that would mean a mismatch of endpoint values between our client and generated.
Ok(Self {
endpoint: endpoint.parse()?,
endpoint: url,
client,
})
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions sdk/storage/azure_storage_blob/tests/blob_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,11 +430,9 @@ async fn test_leased_blob_operations(ctx: TestContext) -> Result<(), Box<dyn Err
#[recorded::test]
async fn test_sassy(ctx: TestContext) -> Result<(), Box<dyn Error>> {
// SAS
let endpoint = "endpoint_with_sas";
let container_name = "acontainer108f32e8";
let blob_name = "goodbye.txt";
let sas_blob_client =
BlobClient::new_no_credential(endpoint, container_name.into(), blob_name.into(), None)?;
let blob_url = "https://vincenttranstock.blob.core.windows.net/acontainer108f32e8/goodbye.txt?sp=racwd&st=2025-07-21T21:22:28Z&se=2025-07-22T05:37:28Z&spr=https&sv=2024-11-04&sr=b&sig=e7QwWlj42eY%2FqDMSdSppRqSSqlkinW00%2Bymu03LAuek%3D";

let sas_blob_client = BlobClient::from_blob_url(blob_url, None)?;

let blob_properties = sas_blob_client.get_properties(None).await?;
let content_length = blob_properties.content_length()?;
Expand Down
Loading