use azure_core::credentials::TokenCredential;
use azure_core::fmt::SafeDebug;
use std::sync::Arc;
#[cfg(feature = "key_auth")]
use azure_core::credentials::Secret;
#[derive(Clone, SafeDebug)]
#[non_exhaustive]
pub enum CosmosCredential {
TokenCredential(Arc<dyn TokenCredential>),
#[cfg(feature = "key_auth")]
MasterKey(Secret),
}
impl From<Arc<dyn TokenCredential>> for CosmosCredential {
fn from(credential: Arc<dyn TokenCredential>) -> Self {
Self::TokenCredential(credential)
}
}
#[cfg(feature = "key_auth")]
impl From<Secret> for CosmosCredential {
fn from(key: Secret) -> Self {
Self::MasterKey(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug)]
struct TestCredential;
#[async_trait::async_trait]
impl azure_core::credentials::TokenCredential for TestCredential {
async fn get_token(
&self,
_scopes: &[&str],
_options: Option<azure_core::credentials::TokenRequestOptions<'_>>,
) -> azure_core::Result<azure_core::credentials::AccessToken> {
unimplemented!("test credential is for Debug-formatting tests only")
}
}
fn assert_safe_debug_render(rendered: &str, variant: &str) {
let payload_redacted = format!("{variant}(..)");
let payload_elided = variant.to_string();
assert!(
rendered == payload_redacted || rendered == payload_elided,
"expected {payload_redacted:?} or {payload_elided:?}, got {rendered:?}"
);
}
#[test]
fn debug_token_credential_redacts_inner_credential() {
let credential: Arc<dyn TokenCredential> = Arc::new(TestCredential);
let cosmos = CosmosCredential::TokenCredential(credential);
assert_safe_debug_render(&format!("{cosmos:?}"), "TokenCredential");
}
#[cfg(feature = "key_auth")]
#[test]
fn debug_master_key_redacts_secret() {
let cosmos = CosmosCredential::MasterKey(Secret::from("super-secret-key"));
let rendered = format!("{cosmos:?}");
assert_safe_debug_render(&rendered, "MasterKey");
assert!(
!rendered.contains("super-secret-key"),
"MasterKey Debug output must not contain the raw key bytes: {rendered:?}"
);
}
}