From fd8f318b89fef7fe0a768782af70e349e4bd1e36 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Mon, 27 Nov 2023 18:13:11 -0500 Subject: [PATCH 1/4] rename AccessToken to Secret and expand it's usage This does 3 things: 1. Renames `AccessToken` to `Secret` 2. Prevents `Debug` of the `AccessToken` from actually showing the secret 3. Starts expanding the use of `Secret` to other areas, such as client certificates --- sdk/core/src/auth.rs | 32 +++++++++++++++---- .../client_credentials_flow/login_response.rs | 8 ++--- .../device_code_flow/device_code_responses.rs | 14 ++++---- .../login_response.rs | 8 ++--- sdk/identity/src/refresh_token.rs | 14 ++++---- .../auto_refreshing_credentials.rs | 24 +++++++------- .../azure_cli_credentials.rs | 6 ++-- .../client_certificate_credentials.rs | 29 ++++++++++------- .../client_secret_credentials.rs | 5 +-- .../token_credentials/default_credentials.rs | 2 ++ .../imds_managed_identity_credentials.rs | 5 +-- .../workload_identity_credentials.rs | 21 +++++++----- sdk/iot_deviceupdate/src/lib.rs | 5 +-- 13 files changed, 103 insertions(+), 70 deletions(-) diff --git a/sdk/core/src/auth.rs b/sdk/core/src/auth.rs index 75bb66188b..ebf13be2d2 100644 --- a/sdk/core/src/auth.rs +++ b/sdk/core/src/auth.rs @@ -4,10 +4,10 @@ use serde::{Deserialize, Serialize}; use std::{borrow::Cow, fmt::Debug}; use time::OffsetDateTime; -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct AccessToken(Cow<'static, str>); +#[derive(Clone, Deserialize, Serialize)] +pub struct Secret(Cow<'static, str>); -impl AccessToken { +impl Secret { pub fn new(access_token: T) -> Self where T: Into>, @@ -20,26 +20,44 @@ impl AccessToken { } } +impl From for Secret { + fn from(access_token: String) -> Self { + Self::new(access_token) + } +} + +impl Debug for Secret { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secret").field(&"").finish() + } +} + /// Represents an Azure service bearer access token with expiry information. #[derive(Debug, Clone)] pub struct TokenResponse { /// Get the access token value. - pub token: AccessToken, + pub token: Secret, /// Gets the time when the provided token expires. pub expires_on: OffsetDateTime, } impl TokenResponse { /// Create a new `TokenResponse`. - pub fn new(token: AccessToken, expires_on: OffsetDateTime) -> Self { - Self { token, expires_on } + pub fn new(token: T, expires_on: OffsetDateTime) -> Self + where + T: Into, + { + Self { + token: token.into(), + expires_on, + } } } /// Represents a credential capable of providing an OAuth token. #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] -pub trait TokenCredential: Send + Sync { +pub trait TokenCredential: Send + Sync + Debug { /// Gets a `TokenResponse` for the specified resource async fn get_token(&self, resource: &str) -> crate::Result; } diff --git a/sdk/identity/src/client_credentials_flow/login_response.rs b/sdk/identity/src/client_credentials_flow/login_response.rs index b56881d915..a43edfce47 100644 --- a/sdk/identity/src/client_credentials_flow/login_response.rs +++ b/sdk/identity/src/client_credentials_flow/login_response.rs @@ -1,4 +1,4 @@ -use azure_core::auth::AccessToken; +use azure_core::auth::Secret; use serde::{Deserialize, Deserializer}; use time::OffsetDateTime; @@ -21,7 +21,7 @@ pub struct LoginResponse { pub expires_on: Option, pub not_before: Option, pub resource: Option, - pub access_token: AccessToken, + pub access_token: Secret, } impl<'de> Deserialize<'de> for LoginResponse { @@ -35,7 +35,7 @@ impl<'de> Deserialize<'de> for LoginResponse { } impl LoginResponse { - pub fn access_token(&self) -> &AccessToken { + pub fn access_token(&self) -> &Secret { &self.access_token } @@ -56,7 +56,7 @@ impl LoginResponse { expires_on, not_before, resource: r.resource, - access_token: AccessToken::new(r.access_token), + access_token: r.access_token.into(), } } } diff --git a/sdk/identity/src/device_code_flow/device_code_responses.rs b/sdk/identity/src/device_code_flow/device_code_responses.rs index 1e45392d1a..e86c4c4d2d 100644 --- a/sdk/identity/src/device_code_flow/device_code_responses.rs +++ b/sdk/identity/src/device_code_flow/device_code_responses.rs @@ -1,4 +1,4 @@ -use azure_core::auth::AccessToken; +use azure_core::auth::Secret; use serde::Deserialize; use std::fmt; @@ -34,26 +34,26 @@ pub struct DeviceCodeAuthorization { pub expires_in: u64, /// Issued for the scopes that were requested. /// Format: Opaque string - access_token: AccessToken, + access_token: Secret, /// Issued if the original scope parameter included offline_access. /// Format: JWT - refresh_token: Option, + refresh_token: Option, /// Issued if the original scope parameter included the openid scope. /// Format: Opaque string - id_token: Option, + id_token: Option, } impl DeviceCodeAuthorization { /// Get the access token - pub fn access_token(&self) -> &AccessToken { + pub fn access_token(&self) -> &Secret { &self.access_token } /// Get the refresh token - pub fn refresh_token(&self) -> Option<&AccessToken> { + pub fn refresh_token(&self) -> Option<&Secret> { self.refresh_token.as_ref() } /// Get the id token - pub fn id_token(&self) -> Option<&AccessToken> { + pub fn id_token(&self) -> Option<&Secret> { self.id_token.as_ref() } } diff --git a/sdk/identity/src/federated_credentials_flow/login_response.rs b/sdk/identity/src/federated_credentials_flow/login_response.rs index af0400a8ac..d9c577e5da 100644 --- a/sdk/identity/src/federated_credentials_flow/login_response.rs +++ b/sdk/identity/src/federated_credentials_flow/login_response.rs @@ -1,4 +1,4 @@ -use azure_core::auth::AccessToken; +use azure_core::auth::Secret; use serde::{Deserialize, Deserializer}; use time::OffsetDateTime; @@ -21,7 +21,7 @@ pub struct LoginResponse { pub expires_on: Option, pub not_before: Option, pub resource: Option, - pub access_token: AccessToken, + pub access_token: Secret, } impl<'de> Deserialize<'de> for LoginResponse { @@ -35,7 +35,7 @@ impl<'de> Deserialize<'de> for LoginResponse { } impl LoginResponse { - pub fn access_token(&self) -> &AccessToken { + pub fn access_token(&self) -> &Secret { &self.access_token } @@ -56,7 +56,7 @@ impl LoginResponse { expires_on, not_before, resource: r.resource, - access_token: AccessToken::new(r.access_token), + access_token: r.access_token.into(), } } } diff --git a/sdk/identity/src/refresh_token.rs b/sdk/identity/src/refresh_token.rs index a84c7dacf0..2cec2ddcee 100644 --- a/sdk/identity/src/refresh_token.rs +++ b/sdk/identity/src/refresh_token.rs @@ -2,7 +2,7 @@ use azure_core::Method; use azure_core::{ - auth::AccessToken, + auth::Secret, content_type, error::{Error, ErrorKind, ResultExt}, headers, HttpClient, Request, @@ -18,7 +18,7 @@ pub async fn exchange( tenant_id: &str, client_id: &str, client_secret: Option<&str>, - refresh_token: &AccessToken, + refresh_token: &Secret, ) -> azure_core::Result { let encoded = { let mut encoded = &mut form_urlencoded::Serializer::new(String::new()); @@ -66,8 +66,8 @@ pub struct RefreshTokenResponse { scopes: Vec, expires_in: u64, ext_expires_in: u64, - access_token: AccessToken, - refresh_token: AccessToken, + access_token: Secret, + refresh_token: Secret, } impl RefreshTokenResponse { @@ -84,11 +84,11 @@ impl RefreshTokenResponse { self.expires_in } /// Issued for the scopes that were requested. - pub fn access_token(&self) -> &AccessToken { + pub fn access_token(&self) -> &Secret { &self.access_token } /// The new refresh token and should replace old refresh token. - pub fn refresh_token(&self) -> &AccessToken { + pub fn refresh_token(&self) -> &Secret { &self.refresh_token } /// Indicates the extended lifetime of an `access_token`. @@ -147,7 +147,7 @@ mod tests { "UNUSED", "UNUSED", None, - &AccessToken::new("UNUSED"), + &Secret::new("UNUSED"), )); } } diff --git a/sdk/identity/src/token_credentials/auto_refreshing_credentials.rs b/sdk/identity/src/token_credentials/auto_refreshing_credentials.rs index 0c6ccc5ac9..9fa890dba4 100644 --- a/sdk/identity/src/token_credentials/auto_refreshing_credentials.rs +++ b/sdk/identity/src/token_credentials/auto_refreshing_credentials.rs @@ -20,7 +20,8 @@ pub struct AutoRefreshingTokenCredential { impl std::fmt::Debug for AutoRefreshingTokenCredential { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("AutoRefreshingTokenCredential") - .field("credential", &"TokenCredential") + .field("credential", &self.credential) + .field("token_cache", &"") .finish() } } @@ -73,10 +74,11 @@ impl TokenCredential for AutoRefreshingTokenCredential { #[cfg(test)] mod tests { use super::*; - use azure_core::auth::AccessToken; + use azure_core::auth::Secret; use azure_core::auth::TokenCredential; use std::sync::Mutex; + #[derive(Debug)] struct MockCredential { token: TokenResponse, get_token_call_count: Mutex, @@ -99,7 +101,7 @@ mod tests { let mut call_count = self.get_token_call_count.lock().unwrap(); *call_count += 1; Ok(TokenResponse { - token: AccessToken::new(format!( + token: Secret::new(format!( "{}-{}:{}", resource, self.token.token.secret(), @@ -117,10 +119,9 @@ mod tests { async fn test_get_token_different_resources() -> azure_core::Result<()> { let resource1 = STORAGE_TOKEN_SCOPE; let resource2 = IOTHUB_TOKEN_SCOPE; - let token_value = "test-token"; - let access_token = AccessToken::new(token_value); + let access_token = "test-token"; let expires_on = OffsetDateTime::now_utc() + Duration::from_secs(300); - let token_response = TokenResponse::new(access_token, expires_on); + let token_response = TokenResponse::new(Secret::new(access_token), expires_on); let mock_credential = MockCredential::new(token_response); let auto_refreshing_credential = @@ -129,7 +130,7 @@ mod tests { // Test that querying a token for the same resource twice returns the same (cached) token on the second call let token1 = auto_refreshing_credential.get_token(resource1).await?; let token2 = auto_refreshing_credential.get_token(resource1).await?; - let expected_token = format!("{}-{}:1", resource1, token_value); + let expected_token = format!("{}-{}:1", resource1, access_token); assert_eq!(token1.token.secret(), expected_token); assert_eq!(token2.token.secret(), expected_token); @@ -137,7 +138,7 @@ mod tests { // Also test that the same token is the returned (cached) on a second call. let token3 = auto_refreshing_credential.get_token(resource2).await?; let token4 = auto_refreshing_credential.get_token(resource2).await?; - let expected_token = format!("{}-{}:2", resource2, token_value); + let expected_token = format!("{}-{}:2", resource2, access_token); assert_eq!(token3.token.secret(), expected_token); assert_eq!(token4.token.secret(), expected_token); @@ -147,10 +148,9 @@ mod tests { #[tokio::test] async fn test_refresh_expired_token() -> azure_core::Result<()> { let resource = STORAGE_TOKEN_SCOPE; - let token_value = "test-token"; - let access_token = AccessToken::new(token_value); + let access_token = "test-token"; let expires_on = OffsetDateTime::now_utc(); - let token_response = TokenResponse::new(access_token, expires_on); + let token_response = TokenResponse::new(Secret::new(access_token), expires_on); let mock_credential = MockCredential::new(token_response); let auto_refreshing_credential = @@ -161,7 +161,7 @@ mod tests { let token = auto_refreshing_credential.get_token(resource).await?; assert_eq!( token.token.secret(), - format!("{}-{}:{}", resource, token_value, i) + format!("{}-{}:{}", resource, access_token, i) ); } diff --git a/sdk/identity/src/token_credentials/azure_cli_credentials.rs b/sdk/identity/src/token_credentials/azure_cli_credentials.rs index df9cbac8ba..c070777320 100644 --- a/sdk/identity/src/token_credentials/azure_cli_credentials.rs +++ b/sdk/identity/src/token_credentials/azure_cli_credentials.rs @@ -1,4 +1,4 @@ -use azure_core::auth::{AccessToken, TokenCredential, TokenResponse}; +use azure_core::auth::{Secret, TokenCredential, TokenResponse}; use azure_core::error::{Error, ErrorKind, ResultExt}; use serde::Deserialize; use std::process::Command; @@ -94,7 +94,7 @@ mod az_cli_date_format { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] struct CliTokenResponse { - pub access_token: AccessToken, + pub access_token: Secret, #[serde(with = "az_cli_date_format")] pub expires_on: OffsetDateTime, pub subscription: String, @@ -104,7 +104,7 @@ struct CliTokenResponse { } /// Enables authentication to Azure Active Directory using Azure CLI to obtain an access token. -#[derive(Default)] +#[derive(Debug, Default)] pub struct AzureCliCredential { _private: (), } diff --git a/sdk/identity/src/token_credentials/client_certificate_credentials.rs b/sdk/identity/src/token_credentials/client_certificate_credentials.rs index 6bd91a8a07..d1a9ed155b 100644 --- a/sdk/identity/src/token_credentials/client_certificate_credentials.rs +++ b/sdk/identity/src/token_credentials/client_certificate_credentials.rs @@ -1,6 +1,6 @@ use super::authority_hosts; use azure_core::{ - auth::{AccessToken, TokenCredential, TokenResponse}, + auth::{Secret, TokenCredential, TokenResponse}, base64, content_type, error::{Error, ErrorKind}, headers, new_http_client, HttpClient, Method, Request, @@ -75,29 +75,34 @@ impl CertificateCredentialOptions { /// /// In order to use subject name validation `send_cert_chain` option must be set to true /// The certificate is expected to be in base64 encoded PKCS12 format +#[derive(Debug)] pub struct ClientCertificateCredential { tenant_id: String, client_id: String, - client_certificate: String, - client_certificate_pass: String, + client_certificate: Secret, + client_certificate_pass: Secret, http_client: Arc, options: CertificateCredentialOptions, } impl ClientCertificateCredential { /// Create a new `ClientCertificateCredential` - pub fn new( + pub fn new( tenant_id: String, client_id: String, - client_certificate: String, - client_certificate_pass: String, + client_certificate: C, + client_certificate_pass: P, options: CertificateCredentialOptions, - ) -> ClientCertificateCredential { + ) -> ClientCertificateCredential + where + C: Into, + P: Into, + { ClientCertificateCredential { tenant_id, client_id, - client_certificate, - client_certificate_pass, + client_certificate: client_certificate.into(), + client_certificate_pass: client_certificate_pass.into(), http_client: new_http_client(), options, } @@ -155,11 +160,11 @@ impl TokenCredential for ClientCertificateCredential { self.tenant_id ); - let certificate = base64::decode(&self.client_certificate) + let certificate = base64::decode(self.client_certificate.secret()) .map_err(|_| Error::message(ErrorKind::Credential, "Base64 decode failed"))?; let certificate = Pkcs12::from_der(&certificate) .map_err(openssl_error)? - .parse2(&self.client_certificate_pass) + .parse2(self.client_certificate_pass.secret()) .map_err(openssl_error)?; let Some(cert) = certificate.cert.as_ref() else { @@ -250,7 +255,7 @@ impl TokenCredential for ClientCertificateCredential { let response: AadTokenResponse = serde_json::from_slice(&rsp_body)?; Ok(TokenResponse::new( - AccessToken::new(response.access_token.to_string()), + response.access_token, OffsetDateTime::now_utc() + Duration::from_secs(response.expires_in), )) } diff --git a/sdk/identity/src/token_credentials/client_secret_credentials.rs b/sdk/identity/src/token_credentials/client_secret_credentials.rs index 70c6499678..aeae279d1a 100644 --- a/sdk/identity/src/token_credentials/client_secret_credentials.rs +++ b/sdk/identity/src/token_credentials/client_secret_credentials.rs @@ -1,5 +1,5 @@ use crate::oauth2_http_client::Oauth2HttpClient; -use azure_core::auth::{AccessToken, TokenCredential, TokenResponse}; +use azure_core::auth::{Secret, TokenCredential, TokenResponse}; use azure_core::error::{ErrorKind, ResultExt}; use azure_core::HttpClient; use oauth2::{basic::BasicClient, AuthType, AuthUrl, Scope, TokenUrl}; @@ -66,6 +66,7 @@ pub mod tenant_ids { /// /// More information on how to configure a client secret can be found here: /// +#[derive(Debug)] pub struct ClientSecretCredential { http_client: Arc, tenant_id: String, @@ -147,7 +148,7 @@ impl TokenCredential for ClientSecretCredential { .map(|r| { use oauth2::TokenResponse as _; TokenResponse::new( - AccessToken::new(r.access_token().secret().to_owned()), + Secret::new(r.access_token().secret().to_owned()), OffsetDateTime::now_utc() + r.expires_in().unwrap_or_default(), ) }) diff --git a/sdk/identity/src/token_credentials/default_credentials.rs b/sdk/identity/src/token_credentials/default_credentials.rs index e59972375b..8b93c71a07 100644 --- a/sdk/identity/src/token_credentials/default_credentials.rs +++ b/sdk/identity/src/token_credentials/default_credentials.rs @@ -76,6 +76,7 @@ impl DefaultAzureCredentialBuilder { } /// Types of `TokenCredential` supported by `DefaultAzureCredential` +#[derive(Debug)] pub enum DefaultAzureCredentialEnum { /// `TokenCredential` from environment variable. Environment(super::EnvironmentCredential), @@ -128,6 +129,7 @@ impl TokenCredential for DefaultAzureCredentialEnum { /// - `ManagedIdentityCredential` /// - `AzureCliCredential` /// Consult the documentation of these credential types for more information on how they attempt authentication. +#[derive(Debug)] pub struct DefaultAzureCredential { sources: Vec, } diff --git a/sdk/identity/src/token_credentials/imds_managed_identity_credentials.rs b/sdk/identity/src/token_credentials/imds_managed_identity_credentials.rs index 775c9887e2..f8db41732f 100644 --- a/sdk/identity/src/token_credentials/imds_managed_identity_credentials.rs +++ b/sdk/identity/src/token_credentials/imds_managed_identity_credentials.rs @@ -1,5 +1,5 @@ use azure_core::{ - auth::{AccessToken, TokenCredential, TokenResponse}, + auth::{Secret, TokenCredential, TokenResponse}, error::{Error, ErrorKind, ResultExt}, HttpClient, Method, Request, StatusCode, }; @@ -21,6 +21,7 @@ const MSI_API_VERSION: &str = "2019-08-01"; /// This authentication type works in Azure VMs, App Service and Azure Functions applications, as well as the Azure Cloud Shell /// /// Built up from docs at [https://docs.microsoft.com/azure/app-service/overview-managed-identity#using-the-rest-protocol](https://docs.microsoft.com/azure/app-service/overview-managed-identity#using-the-rest-protocol) +#[derive(Debug)] pub struct ImdsManagedIdentityCredential { http_client: Arc, object_id: Option, @@ -171,7 +172,7 @@ where #[derive(Debug, Clone, Deserialize)] #[allow(unused)] struct MsiTokenResponse { - pub access_token: AccessToken, + pub access_token: Secret, #[serde(deserialize_with = "expires_on_string")] pub expires_on: OffsetDateTime, pub token_type: String, diff --git a/sdk/identity/src/token_credentials/workload_identity_credentials.rs b/sdk/identity/src/token_credentials/workload_identity_credentials.rs index 5139af0978..267e0a61aa 100644 --- a/sdk/identity/src/token_credentials/workload_identity_credentials.rs +++ b/sdk/identity/src/token_credentials/workload_identity_credentials.rs @@ -1,4 +1,4 @@ -use azure_core::auth::{AccessToken, TokenCredential, TokenResponse}; +use azure_core::auth::{Secret, TokenCredential, TokenResponse}; use azure_core::error::{ErrorKind, ResultExt}; use azure_core::HttpClient; use std::str; @@ -12,27 +12,32 @@ use crate::{federated_credentials_flow, TokenCredentialOptions}; /// /// More information on how to configure a client secret can be found here: /// + +#[derive(Debug)] pub struct WorkloadIdentityCredential { http_client: Arc, tenant_id: String, client_id: String, - token: String, + token: Secret, options: TokenCredentialOptions, } impl WorkloadIdentityCredential { /// Create a new `WorkloadIdentityCredential` - pub fn new( + pub fn new( http_client: Arc, tenant_id: String, client_id: String, - token: String, - ) -> Self { + token: T, + ) -> Self + where + T: Into, + { Self { http_client, tenant_id, client_id, - token, + token: token.into(), options: TokenCredentialOptions::default(), } } @@ -50,7 +55,7 @@ impl TokenCredential for WorkloadIdentityCredential { let res: TokenResponse = federated_credentials_flow::perform( self.http_client.clone(), &self.client_id, - &self.token, + self.token.secret(), &[&format!("{resource}/.default")], &self.tenant_id, self.options.authority_host(), @@ -58,7 +63,7 @@ impl TokenCredential for WorkloadIdentityCredential { .await .map(|r| { TokenResponse::new( - AccessToken::new(r.access_token().secret().to_owned()), + r.access_token().clone(), OffsetDateTime::now_utc() + Duration::from_secs(r.expires_in), ) }) diff --git a/sdk/iot_deviceupdate/src/lib.rs b/sdk/iot_deviceupdate/src/lib.rs index 3edc80124f..90a656f362 100644 --- a/sdk/iot_deviceupdate/src/lib.rs +++ b/sdk/iot_deviceupdate/src/lib.rs @@ -8,7 +8,7 @@ use crate::device_update::UpdateOperation; #[cfg(test)] mod tests { - use azure_core::auth::{AccessToken, TokenCredential, TokenResponse}; + use azure_core::auth::{TokenCredential, TokenResponse}; use azure_core::date; use azure_identity::AutoRefreshingTokenCredential; use std::sync::Arc; @@ -22,6 +22,7 @@ mod tests { } } + #[derive(Debug)] pub(crate) struct MockCredential; #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] @@ -32,7 +33,7 @@ mod tests { _resource: &str, ) -> Result { Ok(TokenResponse::new( - AccessToken::new("TOKEN".to_owned()), + "TOKEN".to_owned(), OffsetDateTime::now_utc() + date::duration_from_days(14), )) } From ef1f691486b9d2f76e7a8fe026c2aad64e183907 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Tue, 28 Nov 2023 00:53:42 -0500 Subject: [PATCH 2/4] use Secret throughout the Storage components as well --- .../src/authorization/authorization_policy.rs | 5 +++-- sdk/storage/src/authorization/mod.rs | 16 ++++++++-------- sdk/storage/src/connection_string.rs | 7 +++++-- sdk/storage/src/hmac.rs | 18 ++++++++++-------- .../src/shared_access_signature/account_sas.rs | 5 +++-- .../src/shared_access_signature/service_sas.rs | 9 +++++---- .../examples/blob_device_code_flow.rs | 2 +- sdk/storage_blobs/src/clients/blob_client.rs | 2 +- .../src/clients/container_client.rs | 2 +- 9 files changed, 37 insertions(+), 29 deletions(-) diff --git a/sdk/storage/src/authorization/authorization_policy.rs b/sdk/storage/src/authorization/authorization_policy.rs index 712e43e486..c18ff427ad 100644 --- a/sdk/storage/src/authorization/authorization_policy.rs +++ b/sdk/storage/src/authorization/authorization_policy.rs @@ -1,5 +1,6 @@ use crate::{clients::ServiceType, StorageCredentials, StorageCredentialsInner}; use azure_core::{ + auth::Secret, error::{ErrorKind, ResultExt}, headers::*, Context, Method, Policy, PolicyResult, Request, @@ -65,7 +66,7 @@ impl Policy for AuthorizationPolicy { } } StorageCredentialsInner::BearerToken(token) => { - request.insert_header(AUTHORIZATION, format!("Bearer {token}")); + request.insert_header(AUTHORIZATION, format!("Bearer {}", token.secret())); } StorageCredentialsInner::TokenCredential(token_credential) => { let bearer_token = token_credential @@ -91,7 +92,7 @@ fn generate_authorization( u: &Url, method: Method, account: &str, - key: &str, + key: &Secret, service_type: ServiceType, ) -> azure_core::Result { let str_to_sign = string_to_sign(h, u, method, account, service_type); diff --git a/sdk/storage/src/authorization/mod.rs b/sdk/storage/src/authorization/mod.rs index 13801893e5..7d6b1124ff 100644 --- a/sdk/storage/src/authorization/mod.rs +++ b/sdk/storage/src/authorization/mod.rs @@ -3,7 +3,7 @@ mod authorization_policy; pub(crate) use self::authorization_policy::AuthorizationPolicy; use crate::clients::{EMULATOR_ACCOUNT, EMULATOR_ACCOUNT_KEY}; use azure_core::{ - auth::TokenCredential, + auth::{Secret, TokenCredential}, error::{ErrorKind, ResultExt}, }; use futures::lock::Mutex; @@ -29,9 +29,9 @@ pub struct StorageCredentials(pub Arc>); #[derive(Clone)] pub enum StorageCredentialsInner { - Key(String, String), + Key(String, Secret), SASToken(Vec<(String, String)>), - BearerToken(String), + BearerToken(Secret), TokenCredential(Arc), Anonymous, } @@ -53,7 +53,7 @@ impl StorageCredentials { pub fn access_key(account: A, key: K) -> Self where A: Into, - K: Into, + K: Into, { Self::wrap(StorageCredentialsInner::Key(account.into(), key.into())) } @@ -86,7 +86,7 @@ impl StorageCredentials { /// ref: pub fn bearer_token(token: T) -> Self where - T: Into, + T: Into, { Self::wrap(StorageCredentialsInner::BearerToken(token.into())) } @@ -129,7 +129,7 @@ impl StorageCredentials { /// Create an Access Key credential for use with the Azure Storage emulator pub fn emulator() -> Self { - Self::access_key(EMULATOR_ACCOUNT, EMULATOR_ACCOUNT_KEY) + Self::access_key(EMULATOR_ACCOUNT, Secret::new(EMULATOR_ACCOUNT_KEY)) } /// Replace the current credentials with new credentials @@ -233,7 +233,7 @@ mod tests { #[tokio::test] async fn test_replacement() -> azure_core::Result<()> { let base = StorageCredentials::anonymous(); - let other = StorageCredentials::bearer_token("foo"); + let other = StorageCredentials::bearer_token(Secret::new("foo")); base.replace(other).await?; @@ -242,7 +242,7 @@ mod tests { let inner = base.0.lock().await; let inner_locked = inner.deref(); assert!( - matches!(&inner_locked, &StorageCredentialsInner::BearerToken(value) if value == "foo") + matches!(&inner_locked, &StorageCredentialsInner::BearerToken(value) if value.secret() == "foo") ); } diff --git a/sdk/storage/src/connection_string.rs b/sdk/storage/src/connection_string.rs index 2ee76661d4..51d2f0c948 100644 --- a/sdk/storage/src/connection_string.rs +++ b/sdk/storage/src/connection_string.rs @@ -1,5 +1,8 @@ use crate::StorageCredentials; -use azure_core::error::{Error, ErrorKind}; +use azure_core::{ + auth::Secret, + error::{Error, ErrorKind}, +}; // Key names. pub const ACCOUNT_KEY_KEY_NAME: &str = "AccountKey"; @@ -193,7 +196,7 @@ impl<'a> ConnectionString<'a> { account_name: Some(account), account_key: Some(key), .. - } => Ok(StorageCredentials::access_key(*account, *key)), + } => Ok(StorageCredentials::access_key(*account, Secret::new(key.to_string()))), _ => { Err(Error::message(ErrorKind::Credential, "Could not create a `StorageCredentail` from the provided connection string. Please validate that you have specified a means of authentication (key, SAS, etc.)." diff --git a/sdk/storage/src/hmac.rs b/sdk/storage/src/hmac.rs index 8e533f0937..9c9af8e0d5 100644 --- a/sdk/storage/src/hmac.rs +++ b/sdk/storage/src/hmac.rs @@ -1,15 +1,16 @@ use azure_core::{ + auth::Secret, base64, error::{ErrorKind, ResultExt}, }; #[cfg(not(feature = "enable_openssl_sign"))] -pub fn sign(data: &str, key: &str) -> azure_core::Result { +pub fn sign(data: &str, key: &Secret) -> azure_core::Result { use hmac::{Hmac, Mac}; use sha2::Sha256; - let mut hmac = Hmac::::new_from_slice(&base64::decode(key)?) + let mut hmac = Hmac::::new_from_slice(&base64::decode(key.secret())?) .with_context(ErrorKind::DataConversion, || { - format!("failed to create hmac from key: {key}") + "failed to create hmac from key".to_string() })?; hmac.update(data.as_bytes()); let signature = hmac.finalize().into_bytes(); @@ -17,9 +18,9 @@ pub fn sign(data: &str, key: &str) -> azure_core::Result { } #[cfg(feature = "enable_openssl_sign")] -pub fn sign(data: &str, key: &str) -> azure_core::Result { +pub fn sign(data: &str, key: &Secret) -> azure_core::Result { use openssl::{error::ErrorStack, hash::MessageDigest, pkey::PKey, sign::Signer}; - let dkey = base64::decode(key)?; + let dkey = base64::decode(key.secret())?; let signature = || -> Result, ErrorStack> { let pkey = PKey::hmac(&dkey)?; let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?; @@ -27,20 +28,21 @@ pub fn sign(data: &str, key: &str) -> azure_core::Result { Ok(signer.sign_to_vec()?) }() .with_context(ErrorKind::DataConversion, || { - format!("failed to create hmac from key: {key}") + "failed to create hmac from key".to_string() })?; Ok(base64::encode(signature)) } #[cfg(test)] mod tests { + use azure_core::auth::Secret; #[test] fn test_hmac_sign() { let data = "create hmac signature for data"; - let key = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; + let key = Secret::new("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - let sig = super::sign(data, key).unwrap(); + let sig = super::sign(data, &key).unwrap(); let expected_sig = "D/y9XyIEdUzEbdV570h8dou/mfkbMA1lKCOPqPDPAd0="; assert_eq!(sig, expected_sig); diff --git a/sdk/storage/src/shared_access_signature/account_sas.rs b/sdk/storage/src/shared_access_signature/account_sas.rs index df3cb00303..d602d4ee03 100644 --- a/sdk/storage/src/shared_access_signature/account_sas.rs +++ b/sdk/storage/src/shared_access_signature/account_sas.rs @@ -2,6 +2,7 @@ use crate::{ hmac::sign, shared_access_signature::{format_date, SasProtocol, SasToken}, }; +use azure_core::auth::Secret; use std::fmt; use time::OffsetDateTime; use url::form_urlencoded; @@ -132,7 +133,7 @@ impl fmt::Display for AccountSasPermissions { pub struct AccountSharedAccessSignature { account: String, - key: String, + key: Secret, version: AccountSasVersion, resource: AccountSasResource, resource_type: AccountSasResourceType, @@ -146,7 +147,7 @@ pub struct AccountSharedAccessSignature { impl AccountSharedAccessSignature { pub fn new( account: String, - key: String, + key: Secret, resource: AccountSasResource, resource_type: AccountSasResourceType, expiry: OffsetDateTime, diff --git a/sdk/storage/src/shared_access_signature/service_sas.rs b/sdk/storage/src/shared_access_signature/service_sas.rs index 33e6f4a322..270f1efc65 100644 --- a/sdk/storage/src/shared_access_signature/service_sas.rs +++ b/sdk/storage/src/shared_access_signature/service_sas.rs @@ -2,6 +2,7 @@ use crate::{ hmac, shared_access_signature::{format_date, SasProtocol, SasToken}, }; +use azure_core::auth::Secret; use std::fmt; use time::OffsetDateTime; use url::form_urlencoded; @@ -93,7 +94,7 @@ impl fmt::Display for BlobSasPermissions { } pub struct BlobSharedAccessSignature { - key: String, + key: Secret, canonicalized_resource: String, resource: BlobSignedResource, permissions: BlobSasPermissions, // sp @@ -107,7 +108,7 @@ pub struct BlobSharedAccessSignature { impl BlobSharedAccessSignature { pub fn new( - key: String, + key: Secret, canonicalized_resource: String, permissions: BlobSasPermissions, expiry: OffsetDateTime, @@ -209,7 +210,7 @@ mod test { ..Default::default() }; let signed_token = BlobSharedAccessSignature::new( - String::from(MOCK_SECRET_KEY), + Secret::new(MOCK_SECRET_KEY), String::from(MOCK_CANONICALIZED_RESOURCE), permissions, OffsetDateTime::UNIX_EPOCH + Duration::days(7), @@ -235,7 +236,7 @@ mod test { ..Default::default() }; let signed_token = BlobSharedAccessSignature::new( - String::from(MOCK_SECRET_KEY), + Secret::new(MOCK_SECRET_KEY), String::from(MOCK_CANONICALIZED_RESOURCE), permissions, OffsetDateTime::UNIX_EPOCH + Duration::days(7), diff --git a/sdk/storage_blobs/examples/blob_device_code_flow.rs b/sdk/storage_blobs/examples/blob_device_code_flow.rs index e5d4198842..be2c558702 100644 --- a/sdk/storage_blobs/examples/blob_device_code_flow.rs +++ b/sdk/storage_blobs/examples/blob_device_code_flow.rs @@ -59,7 +59,7 @@ async fn main() -> azure_core::Result<()> { // we can now spend the access token in other crates. In this example we are // creating an Azure Storage client using the access token. let storage_credentials = - StorageCredentials::bearer_token(authorization.access_token().secret()); + StorageCredentials::bearer_token(authorization.access_token().clone()); let blob_service_client = BlobServiceClient::new(storage_account_name, storage_credentials); // now we enumerate the containers in the specified storage account. diff --git a/sdk/storage_blobs/src/clients/blob_client.rs b/sdk/storage_blobs/src/clients/blob_client.rs index bf8b94949b..4315a114b7 100644 --- a/sdk/storage_blobs/src/clients/blob_client.rs +++ b/sdk/storage_blobs/src/clients/blob_client.rs @@ -250,7 +250,7 @@ impl BlobClient { self.blob_name() ); Ok(BlobSharedAccessSignature::new( - key.to_string(), + key.clone(), canonicalized_resource, permissions, expiry, diff --git a/sdk/storage_blobs/src/clients/container_client.rs b/sdk/storage_blobs/src/clients/container_client.rs index fe661517ac..6dca6135e3 100644 --- a/sdk/storage_blobs/src/clients/container_client.rs +++ b/sdk/storage_blobs/src/clients/container_client.rs @@ -133,7 +133,7 @@ impl ContainerClient { let canonicalized_resource = format!("/blob/{}/{}", account, self.container_name(),); Ok(BlobSharedAccessSignature::new( - key.to_string(), + key.clone(), canonicalized_resource, permissions, expiry, From 73017a11bda493103af76424ccb3b262eb1e2903 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Tue, 28 Nov 2023 10:04:01 -0500 Subject: [PATCH 3/4] fix doctest --- sdk/storage/src/authorization/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/src/authorization/mod.rs b/sdk/storage/src/authorization/mod.rs index 7d6b1124ff..c53084ef3b 100644 --- a/sdk/storage/src/authorization/mod.rs +++ b/sdk/storage/src/authorization/mod.rs @@ -22,7 +22,7 @@ use url::Url; /// /// For example, to use an account name and access key: /// ```rust -/// azure_storage::StorageCredentials::access_key("my_account", "SOMEACCESSKEY"); +/// azure_storage::StorageCredentials::access_key("my_account", azure_core::auth::Secret::new("SOMEACCESSKEY")); /// ``` #[derive(Clone)] pub struct StorageCredentials(pub Arc>); From e4a26971dfc25e6c1898da7253f9d7d3a3c7c815 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Wed, 29 Nov 2023 10:17:51 -0500 Subject: [PATCH 4/4] fix issues stemming from merging from main --- sdk/core/src/hmac.rs | 4 ++-- sdk/iot_hub/src/service/mod.rs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sdk/core/src/hmac.rs b/sdk/core/src/hmac.rs index c1c7e0cd1b..992c179976 100644 --- a/sdk/core/src/hmac.rs +++ b/sdk/core/src/hmac.rs @@ -1,6 +1,6 @@ +use crate::auth::Secret; #[cfg(any(feature = "hmac_rust", feature = "hmac_openssl"))] use crate::{ - auth::Secret, base64, error::{ErrorKind, ResultExt}, }; @@ -38,7 +38,7 @@ pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result { } #[cfg(not(any(feature = "hmac_rust", feature = "hmac_openssl")))] -pub fn hmac_sha256(_data: &str, _key: &str) -> crate::Result { +pub fn hmac_sha256(_data: &str, _key: &Secret) -> crate::Result { unimplemented!("An HMAC signing request was called without an hmac implementation. Make sure to enable either the `hmac_rust` or `hmac_openssl` feature"); } diff --git a/sdk/iot_hub/src/service/mod.rs b/sdk/iot_hub/src/service/mod.rs index 2af23fdb16..6d6deafd25 100644 --- a/sdk/iot_hub/src/service/mod.rs +++ b/sdk/iot_hub/src/service/mod.rs @@ -136,10 +136,11 @@ impl ServiceClient { /// ``` /// use std::sync::Arc; /// use azure_iot_hub::service::ServiceClient; + /// use azure_core::auth::Secret; /// /// let iot_hub_name = "iot-hub"; /// let key_name = "iot_hubowner"; - /// let private_key = "YSB2ZXJ5IHNlY3VyZSBrZXkgaXMgaW1wb3J0YW50Cg=="; + /// let private_key = Secret::new("YSB2ZXJ5IHNlY3VyZSBrZXkgaXMgaW1wb3J0YW50Cg=="); /// /// let result = ServiceClient::new_private_key(iot_hub_name, key_name, private_key, 3600); /// assert!(result.is_ok()); @@ -153,14 +154,15 @@ impl ServiceClient { where S: Into, T: AsRef, - U: AsRef, + U: Into, { let iot_hub_name_str = iot_hub_name.into(); + let private_key = private_key.into(); let sas_token = Self::generate_sas_token( iot_hub_name_str.as_str(), key_name.as_ref(), - private_key.as_ref(), + &private_key, expires_in_seconds, )?;