From e7c28453e5979e611786671556a1b6688f3a8789 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Wed, 29 Nov 2023 10:55:32 -0500 Subject: [PATCH] rename AccessToken to Secret and expand it's usage (#1472) --- sdk/core/src/auth.rs | 50 ++++++++++++++++--- sdk/core/src/hmac.rs | 16 +++--- .../permission/authorization_token.rs | 6 +-- .../resources/permission/permission_token.rs | 6 +-- .../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 +- sdk/iot_hub/src/service/mod.rs | 17 ++++--- .../src/service_bus/mod.rs | 15 +++--- .../src/service_bus/queue_client.rs | 6 +-- .../src/service_bus/topic_client.rs | 6 +-- .../src/authorization/authorization_policy.rs | 5 +- sdk/storage/src/authorization/mod.rs | 18 +++---- sdk/storage/src/connection_string.rs | 7 ++- .../shared_access_signature/account_sas.rs | 6 +-- .../shared_access_signature/service_sas.rs | 10 ++-- .../examples/blob_device_code_flow.rs | 2 +- sdk/storage_blobs/src/clients/blob_client.rs | 2 +- .../src/clients/container_client.rs | 2 +- 28 files changed, 188 insertions(+), 127 deletions(-) diff --git a/sdk/core/src/auth.rs b/sdk/core/src/auth.rs index 75bb66188b..04822ad5c4 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, Eq)] +pub struct Secret(Cow<'static, str>); -impl AccessToken { +impl Secret { pub fn new(access_token: T) -> Self where T: Into>, @@ -20,26 +20,62 @@ impl AccessToken { } } +// NOTE: this is a constant time compare, however LLVM may (and probably will) +// optimize this in unexpected ways. +impl PartialEq for Secret { + fn eq(&self, other: &Self) -> bool { + let a = self.secret(); + let b = other.secret(); + + if a.len() != b.len() { + return false; + } + + a.bytes() + .zip(b.bytes()) + .fold(0, |acc, (a, b)| acc | (a ^ b)) + == 0 + } +} + +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/core/src/hmac.rs b/sdk/core/src/hmac.rs index 39b5f7455e..992c179976 100644 --- a/sdk/core/src/hmac.rs +++ b/sdk/core/src/hmac.rs @@ -1,3 +1,4 @@ +use crate::auth::Secret; #[cfg(any(feature = "hmac_rust", feature = "hmac_openssl"))] use crate::{ base64, @@ -6,10 +7,10 @@ use crate::{ // if both hmac_rust and hmac_openssl are enabled, use hmac_openssl #[cfg(all(feature = "hmac_rust", not(feature = "hmac_openssl")))] -pub fn hmac_sha256(data: &str, key: &str) -> crate::Result { +pub fn hmac_sha256(data: &str, key: &Secret) -> crate::Result { use hmac::{Hmac, Mac}; use sha2::Sha256; - let key = base64::decode(key)?; + let key = base64::decode(key.secret())?; let mut hmac = Hmac::::new_from_slice(&key) .with_context(ErrorKind::DataConversion, || { "failed to create hmac from key" @@ -20,10 +21,10 @@ pub fn hmac_sha256(data: &str, key: &str) -> crate::Result { } #[cfg(feature = "hmac_openssl")] -pub fn hmac_sha256(data: &str, key: &str) -> crate::Result { +pub fn hmac_sha256(data: &str, key: &Secret) -> crate::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)?; @@ -37,19 +38,20 @@ pub fn hmac_sha256(data: &str, key: &str) -> 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"); } #[cfg(test)] mod tests { + use super::*; #[test] fn test_hmac_sign() { let data = "create hmac signature for data"; - let key = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"; + let key = Secret::new("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - let sig = super::hmac_sha256(data, key).unwrap(); + let sig = hmac_sha256(data, &key).unwrap(); let expected_sig = "D/y9XyIEdUzEbdV570h8dou/mfkbMA1lKCOPqPDPAd0="; assert_eq!(sig, expected_sig); diff --git a/sdk/data_cosmos/src/resources/permission/authorization_token.rs b/sdk/data_cosmos/src/resources/permission/authorization_token.rs index 6babb0fd07..1a49710266 100644 --- a/sdk/data_cosmos/src/resources/permission/authorization_token.rs +++ b/sdk/data_cosmos/src/resources/permission/authorization_token.rs @@ -1,5 +1,5 @@ use super::PermissionToken; -use azure_core::auth::TokenCredential; +use azure_core::auth::{Secret, TokenCredential}; use std::fmt; use std::sync::Arc; @@ -9,7 +9,7 @@ use std::sync::Arc; #[derive(Clone)] pub enum AuthorizationToken { /// Used for administrative resources: database accounts, databases, users, and permissions - PrimaryKey(String), + PrimaryKey(Secret), /// Used for application resources: containers, documents, attachments, stored procedures, triggers, and UDFs Resource(String), /// AAD token credential @@ -24,7 +24,7 @@ impl AuthorizationToken { where S: Into, { - Ok(AuthorizationToken::PrimaryKey(key.into())) + Ok(AuthorizationToken::PrimaryKey(Secret::new(key.into()))) } /// Create a resource `AuthorizationToken` for the given resource. diff --git a/sdk/data_cosmos/src/resources/permission/permission_token.rs b/sdk/data_cosmos/src/resources/permission/permission_token.rs index 9661bba429..2668253fca 100644 --- a/sdk/data_cosmos/src/resources/permission/permission_token.rs +++ b/sdk/data_cosmos/src/resources/permission/permission_token.rs @@ -1,5 +1,5 @@ use super::AuthorizationToken; -use azure_core::base64; +use azure_core::{auth::Secret, base64}; const PERMISSION_TYPE_PREFIX: &str = "type="; const VERSION_PREFIX: &str = "ver="; @@ -42,7 +42,7 @@ impl std::fmt::Display for PermissionToken { use std::borrow::Cow; let (permission_type, signature) = match &self.token { AuthorizationToken::Resource(s) => ("resource", Cow::Borrowed(s)), - AuthorizationToken::PrimaryKey(s) => ("master", Cow::Owned(base64::encode(s))), + AuthorizationToken::PrimaryKey(s) => ("master", Cow::Owned(base64::encode(s.secret()))), AuthorizationToken::TokenCredential(_) => { panic!("TokenCredential not supported for PermissionToken") } @@ -86,7 +86,7 @@ impl std::convert::TryFrom<&str> for PermissionToken { let permission_type = try_get_item(s, &parts, PERMISSION_TYPE_PREFIX)?; let signature = try_get_item(s, &parts, SIGNATURE_PREFIX)?.to_owned(); let token = match permission_type { - "master" => AuthorizationToken::PrimaryKey(signature), + "master" => AuthorizationToken::PrimaryKey(Secret::new(signature)), "resource" => AuthorizationToken::Resource(signature), _ => { return Err(PermissionTokenParseError::UnrecognizedPermissionType { 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), )) } diff --git a/sdk/iot_hub/src/service/mod.rs b/sdk/iot_hub/src/service/mod.rs index d9cb0f0a44..6d6deafd25 100644 --- a/sdk/iot_hub/src/service/mod.rs +++ b/sdk/iot_hub/src/service/mod.rs @@ -1,4 +1,5 @@ use crate::authorization_policy::AuthorizationPolicy; +use azure_core::auth::Secret; use azure_core::error::{Error, ErrorKind, ResultExt}; use azure_core::hmac::hmac_sha256; use azure_core::request_options::ContentType; @@ -107,7 +108,7 @@ impl ServiceClient { fn generate_sas_token( iot_hub_name: &str, key_name: &str, - private_key: &str, + private_key: &Secret, expires_in_seconds: u64, ) -> azure_core::Result { let expiry_date = OffsetDateTime::now_utc() + Duration::from_secs(expires_in_seconds); @@ -135,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()); @@ -152,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, )?; @@ -196,7 +199,7 @@ impl ServiceClient { let parts: Vec<&str> = connection_string.as_ref().split(';').collect(); let mut iot_hub_name: Option<&str> = None; let mut key_name: Option<&str> = None; - let mut primary_key: Option<&str> = None; + let mut primary_key: Option = None; if parts.len() != 3 { return Err(Error::message( @@ -223,7 +226,7 @@ impl ServiceClient { } if val.contains("SharedAccessKey=") { - primary_key = Some(&val[start..]); + primary_key = Some(Secret::new(val[start..].to_owned())); } } @@ -249,7 +252,7 @@ impl ServiceClient { })?; let sas_token = - Self::generate_sas_token(iot_hub_name, key_name, primary_key, expires_in_seconds) + Self::generate_sas_token(iot_hub_name, key_name, &primary_key, expires_in_seconds) .context(ErrorKind::Other, "generate SAS token error")?; let pipeline = new_pipeline_from_options( diff --git a/sdk/messaging_servicebus/src/service_bus/mod.rs b/sdk/messaging_servicebus/src/service_bus/mod.rs index ea570c6897..e8deb64fd0 100644 --- a/sdk/messaging_servicebus/src/service_bus/mod.rs +++ b/sdk/messaging_servicebus/src/service_bus/mod.rs @@ -1,3 +1,4 @@ +use azure_core::auth::Secret; use azure_core::hmac::hmac_sha256; use azure_core::{ error::Error, headers, CollectedResponse, HttpClient, Method, Request, StatusCode, Url, @@ -26,7 +27,7 @@ fn finalize_request( method: azure_core::Method, body: Option, policy_name: &str, - signing_key: &str, + signing_key: &Secret, ) -> azure_core::Result { // generate sas auth let sas = generate_signature( @@ -56,7 +57,7 @@ fn finalize_request( /// Generates a SAS signature fn generate_signature( policy_name: &str, - signing_key: &str, + signing_key: &Secret, url: &str, ttl: Duration, ) -> azure_core::Result { @@ -85,7 +86,7 @@ async fn send_message( namespace: &str, queue_or_topic: &str, policy_name: &str, - signing_key: &str, + signing_key: &Secret, msg: &str, ) -> azure_core::Result<()> { let url = format!("https://{namespace}.servicebus.windows.net/{queue_or_topic}/messages"); @@ -111,7 +112,7 @@ async fn receive_and_delete_message( namespace: &str, queue_or_topic: &str, policy_name: &str, - signing_key: &str, + signing_key: &Secret, subscription: Option<&str>, ) -> azure_core::Result { let url = get_head_url(namespace, queue_or_topic, subscription); @@ -136,7 +137,7 @@ async fn peek_lock_message( namespace: &str, queue_or_topic: &str, policy_name: &str, - signing_key: &str, + signing_key: &Secret, lock_expiry: Option, subscription: Option<&str>, ) -> azure_core::Result { @@ -159,7 +160,7 @@ async fn peek_lock_message2( namespace: &str, queue_or_topic: &str, policy_name: &str, - signing_key: &str, + signing_key: &Secret, lock_expiry: Option, subscription: Option<&str>, ) -> azure_core::Result { @@ -200,7 +201,7 @@ pub struct PeekLockResponse { status: StatusCode, http_client: Arc, policy_name: String, - signing_key: String, + signing_key: Secret, } impl PeekLockResponse { diff --git a/sdk/messaging_servicebus/src/service_bus/queue_client.rs b/sdk/messaging_servicebus/src/service_bus/queue_client.rs index c1871e866e..3cdb30e1b7 100644 --- a/sdk/messaging_servicebus/src/service_bus/queue_client.rs +++ b/sdk/messaging_servicebus/src/service_bus/queue_client.rs @@ -9,7 +9,7 @@ use crate::{ }; use std::time::Duration; -use azure_core::{error::Error, HttpClient}; +use azure_core::{auth::Secret, error::Error, HttpClient}; /// Client object that allows interaction with the `ServiceBus` API #[derive(Debug, Clone)] @@ -18,7 +18,7 @@ pub struct QueueClient { namespace: String, queue: String, policy_name: String, - signing_key: String, + signing_key: Secret, } impl QueueClient { @@ -34,7 +34,7 @@ impl QueueClient { N: Into, Q: Into, P: Into, - K: Into, + K: Into, { Ok(QueueClient { http_client, diff --git a/sdk/messaging_servicebus/src/service_bus/topic_client.rs b/sdk/messaging_servicebus/src/service_bus/topic_client.rs index dd0697688b..246e163c5f 100644 --- a/sdk/messaging_servicebus/src/service_bus/topic_client.rs +++ b/sdk/messaging_servicebus/src/service_bus/topic_client.rs @@ -9,7 +9,7 @@ use crate::{ }; use std::time::Duration; -use azure_core::{error::Error, HttpClient}; +use azure_core::{auth::Secret, error::Error, HttpClient}; /// Client object that allows interaction with the `ServiceBus` API #[derive(Debug, Clone)] @@ -18,7 +18,7 @@ pub struct TopicClient { namespace: String, topic: String, policy_name: String, - signing_key: String, + signing_key: Secret, } #[derive(Debug, Clone)] @@ -45,7 +45,7 @@ impl TopicClient { N: Into, T: Into, P: Into, - K: Into, + K: Into, { Ok(Self { http_client, diff --git a/sdk/storage/src/authorization/authorization_policy.rs b/sdk/storage/src/authorization/authorization_policy.rs index 46e97113a9..5a74377590 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::*, hmac::hmac_sha256, @@ -66,7 +67,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 @@ -92,7 +93,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..c53084ef3b 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; @@ -22,16 +22,16 @@ 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>); #[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/shared_access_signature/account_sas.rs b/sdk/storage/src/shared_access_signature/account_sas.rs index 627eb09a5a..3382e25cce 100644 --- a/sdk/storage/src/shared_access_signature/account_sas.rs +++ b/sdk/storage/src/shared_access_signature/account_sas.rs @@ -1,5 +1,5 @@ use crate::shared_access_signature::{format_date, SasProtocol, SasToken}; -use azure_core::hmac::hmac_sha256; +use azure_core::{auth::Secret, hmac::hmac_sha256}; use std::fmt; use time::OffsetDateTime; use url::form_urlencoded; @@ -130,7 +130,7 @@ impl fmt::Display for AccountSasPermissions { pub struct AccountSharedAccessSignature { account: String, - key: String, + key: Secret, version: AccountSasVersion, resource: AccountSasResource, resource_type: AccountSasResourceType, @@ -144,7 +144,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 5fd989a6d9..c4fae4bcc7 100644 --- a/sdk/storage/src/shared_access_signature/service_sas.rs +++ b/sdk/storage/src/shared_access_signature/service_sas.rs @@ -1,5 +1,5 @@ use crate::shared_access_signature::{format_date, SasProtocol, SasToken}; -use azure_core::hmac::hmac_sha256; +use azure_core::{auth::Secret, hmac::hmac_sha256}; use std::fmt; use time::OffsetDateTime; use url::form_urlencoded; @@ -91,7 +91,7 @@ impl fmt::Display for BlobSasPermissions { } pub struct BlobSharedAccessSignature { - key: String, + key: Secret, canonicalized_resource: String, resource: BlobSignedResource, permissions: BlobSasPermissions, // sp @@ -105,7 +105,7 @@ pub struct BlobSharedAccessSignature { impl BlobSharedAccessSignature { pub fn new( - key: String, + key: Secret, canonicalized_resource: String, permissions: BlobSasPermissions, expiry: OffsetDateTime, @@ -207,7 +207,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), @@ -233,7 +233,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,