cloud_file_signer/azure/
mod.rs1use std::time::{Duration, SystemTime};
4
5use azure_storage::prelude::*;
6use azure_storage_blobs::prelude::*;
7
8use crate::{CloudFileSigner, Permission, PresignedUrl, SignerError};
9mod uri;
10
11use self::uri::AzureUri;
12
13#[derive(Debug, Clone)]
15pub struct AbfsFileSigner {
16 storage_account: String,
17 client_builder: ClientBuilder,
18}
19
20impl AbfsFileSigner {
21 pub fn new<A: Into<String>, C: Into<StorageCredentials>>(
23 storage_account: A,
24 storage_credentials: C,
25 ) -> Self {
26 let storage_account_name = storage_account.into();
27 let client_builder = ClientBuilder::new(storage_account_name.clone(), storage_credentials);
28 Self {
29 storage_account: storage_account_name,
30 client_builder,
31 }
32 }
33
34 pub fn from_client_builder<A: Into<String>>(
36 storage_account: A,
37 client_builder: ClientBuilder,
38 ) -> Self {
39 let storage_account_name = storage_account.into();
40 Self {
41 storage_account: storage_account_name,
42 client_builder,
43 }
44 }
45
46 #[must_use]
49 pub fn storage_account(&self) -> &str {
50 &self.storage_account
51 }
52
53 fn client_builder(&self) -> ClientBuilder {
54 self.client_builder.clone()
55 }
56
57 async fn sign_read_request(
58 &self,
59 uri: &AzureUri,
60 expiration: Duration,
61 ) -> Result<PresignedUrl, SignerError> {
62 if uri.storage_account() != self.storage_account() {
63 return Err(SignerError::other_error(
64 "Storage account name in URI does not match signer",
65 ));
66 }
67
68 let valid_from = SystemTime::now();
69 let permissions = BlobSasPermissions {
70 read: true,
71 ..Default::default()
72 };
73 let expiry = time::OffsetDateTime::now_utc() + expiration;
74
75 let blob_client = self
76 .client_builder()
77 .blob_client(uri.container(), uri.blob());
78 let sas_token = blob_client
79 .shared_access_signature(permissions, expiry)
80 .await
81 .unwrap();
82 let sas_token = sas_token.start(time::OffsetDateTime::now_utc());
83
84 let signed_url = blob_client.generate_signed_blob_url(&sas_token).unwrap();
85 Ok(PresignedUrl::new(signed_url, valid_from, expiration))
86 }
87}
88
89#[async_trait::async_trait]
90impl CloudFileSigner for AbfsFileSigner {
91 async fn sign(
92 &self,
93 path: &str,
94 _valid_from: SystemTime,
95 expiration: Duration,
96 permission: Permission,
97 ) -> Result<PresignedUrl, SignerError> {
98 tracing::info!("signing path: {}", path);
99 let azure_uri = path.parse::<AzureUri>()?;
100 match permission {
101 Permission::Read => Ok(self.sign_read_request(&azure_uri, expiration).await?),
102 _ => Err(SignerError::permission_not_supported(format!(
103 "permission {permission:?} not supported"
104 ))),
105 }
106 }
107}