Skip to content

Commit 462954f

Browse files
authored
add AutoRefreshingTokenCredential support to StorageAccountClient (#757)
1 parent 9be18ba commit 462954f

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

sdk/storage/src/core/clients/storage_account_client.rs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
AccountSharedAccessSignatureBuilder, ClientAccountSharedAccessSignature,
77
},
88
};
9+
use azure_core::auth::TokenCredential;
910
use azure_core::headers::*;
1011
use azure_core::HttpClient;
1112
use bytes::Bytes;
@@ -26,15 +27,29 @@ pub const EMULATOR_ACCOUNT: &str = "devstoreaccount1";
2627
pub const EMULATOR_ACCOUNT_KEY: &str =
2728
"Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
2829

30+
pub const STORAGE_TOKEN_SCOPE: &str = "https://storage.azure.com/";
31+
2932
const HEADER_VERSION: &str = "x-ms-version";
3033

3134
const AZURE_VERSION: &str = "2019-12-12";
3235

33-
#[derive(Debug, Clone, PartialEq, Eq)]
3436
pub enum StorageCredentials {
3537
Key(String, String),
3638
SASToken(Vec<(String, String)>),
3739
BearerToken(String),
40+
TokenCredential(Box<dyn TokenCredential>),
41+
}
42+
43+
impl std::fmt::Debug for StorageCredentials {
44+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45+
match &self {
46+
StorageCredentials::TokenCredential(_) => f
47+
.debug_struct("StorageCredentials")
48+
.field("credential", &"TokenCredential")
49+
.finish(),
50+
_ => self.fmt(f),
51+
}
52+
}
3853
}
3954

4055
#[derive(Debug, Clone, Copy)]
@@ -45,7 +60,7 @@ pub enum ServiceType {
4560
Table,
4661
}
4762

48-
#[derive(Debug, Clone)]
63+
#[derive(Debug)]
4964
pub struct StorageAccountClient {
5065
storage_credentials: StorageCredentials,
5166
http_client: Arc<dyn HttpClient>,
@@ -236,6 +251,36 @@ impl StorageAccountClient {
236251
})
237252
}
238253

254+
pub fn new_token_credential<A>(
255+
http_client: Arc<dyn HttpClient>,
256+
account: A,
257+
token_credential: Box<dyn TokenCredential>,
258+
) -> Arc<Self>
259+
where
260+
A: Into<String>,
261+
{
262+
let account = account.into();
263+
264+
Arc::new(Self {
265+
blob_storage_url: Url::parse(&format!("https://{}.blob.core.windows.net", &account))
266+
.unwrap(),
267+
table_storage_url: Url::parse(&format!("https://{}.table.core.windows.net", &account))
268+
.unwrap(),
269+
queue_storage_url: Url::parse(&format!("https://{}.queue.core.windows.net", &account))
270+
.unwrap(),
271+
queue_storage_secondary_url: Url::parse(&format!(
272+
"https://{}-secondary.queue.core.windows.net",
273+
&account
274+
))
275+
.unwrap(),
276+
filesystem_url: Url::parse(&format!("https://{}.dfs.core.windows.net", &account))
277+
.unwrap(),
278+
storage_credentials: StorageCredentials::TokenCredential(token_credential),
279+
http_client,
280+
account,
281+
})
282+
}
283+
239284
pub fn new_connection_string(
240285
http_client: Arc<dyn HttpClient>,
241286
connection_string: &str,
@@ -407,6 +452,15 @@ impl StorageAccountClient {
407452
StorageCredentials::BearerToken(token) => {
408453
request.header(AUTHORIZATION, format!("Bearer {}", token))
409454
}
455+
StorageCredentials::TokenCredential(token_credential) => {
456+
let bearer_token_future = token_credential.get_token(STORAGE_TOKEN_SCOPE);
457+
let bearer_token = futures::executor::block_on(bearer_token_future)?;
458+
459+
request.header(
460+
AUTHORIZATION,
461+
format!("Bearer {}", bearer_token.token.secret()),
462+
)
463+
}
410464
};
411465

412466
let request = if let Some(request_body) = request_body {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#[macro_use]
2+
extern crate log;
3+
4+
use azure_identity::token_credentials::AutoRefreshingTokenCredential;
5+
use azure_identity::token_credentials::DefaultAzureCredential;
6+
use azure_storage::core::prelude::*;
7+
use azure_storage_blobs::prelude::*;
8+
use std::error::Error;
9+
10+
#[tokio::main]
11+
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
12+
env_logger::init();
13+
// First we retrieve the account name, container and blob name from command line args
14+
15+
let account = std::env::args()
16+
.nth(1)
17+
.expect("please specify the account name as first command line parameter");
18+
let container = std::env::args()
19+
.nth(2)
20+
.expect("please specify the container name as second command line parameter");
21+
let blob = std::env::args()
22+
.nth(3)
23+
.expect("please specify the blob name as third command line parameter");
24+
25+
let creds = std::sync::Arc::new(DefaultAzureCredential::default());
26+
let auto_creds = Box::new(AutoRefreshingTokenCredential::new(creds));
27+
28+
let http_client = azure_core::new_http_client();
29+
let blob_client =
30+
StorageAccountClient::new_token_credential(http_client.clone(), &account, auto_creds)
31+
.as_container_client(&container)
32+
.as_blob_client(&blob);
33+
34+
trace!("Requesting blob");
35+
36+
let response = blob_client.get().execute().await?;
37+
38+
let s_content = String::from_utf8(response.data.to_vec())?;
39+
println!("blob == {:?}", blob);
40+
println!("s_content == {}", s_content);
41+
42+
Ok(())
43+
}

0 commit comments

Comments
 (0)