use adk_core::{AdkError, ErrorCategory, ErrorComponent};
use async_trait::async_trait;
use azure_security_keyvault_secrets::SecretClient;
use std::sync::Arc;
use super::provider::SecretProvider;
pub struct AzureSecretProvider {
client: SecretClient,
}
impl AzureSecretProvider {
pub fn new(
vault_url: &str,
credential: Arc<dyn azure_core::credentials::TokenCredential>,
) -> Result<Self, AdkError> {
let client = SecretClient::new(vault_url, credential, None).map_err(|err| {
AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Internal,
"auth.azure_keyvault.invalid_config",
format!("failed to create Azure Key Vault client: {err}"),
)
.with_provider("azure-keyvault")
})?;
Ok(Self { client })
}
pub fn from_client(client: SecretClient) -> Self {
Self { client }
}
}
fn map_azure_error(err: azure_core::Error) -> AdkError {
use azure_core::error::ErrorKind;
match err.kind() {
ErrorKind::HttpResponse { status, .. } => {
let status_code = u16::from(*status);
match status_code {
401 | 403 => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Unauthorized,
"auth.azure_keyvault.unauthorized",
format!("Azure Key Vault authentication failed: {err}"),
)
.with_provider("azure-keyvault")
.with_upstream_status(status_code),
404 => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::NotFound,
"auth.azure_keyvault.not_found",
format!("Azure Key Vault secret not found: {err}"),
)
.with_provider("azure-keyvault")
.with_upstream_status(status_code),
_ => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Internal,
"auth.azure_keyvault.service_error",
format!("Azure Key Vault service error: {err}"),
)
.with_provider("azure-keyvault")
.with_upstream_status(status_code),
}
}
ErrorKind::Credential => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Unauthorized,
"auth.azure_keyvault.credential",
format!("Azure Key Vault credential error: {err}"),
)
.with_provider("azure-keyvault"),
ErrorKind::Io => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Unavailable,
"auth.azure_keyvault.network",
format!("Azure Key Vault network error: {err}"),
)
.with_provider("azure-keyvault"),
_ => AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Internal,
"auth.azure_keyvault.unknown",
format!("Azure Key Vault error: {err}"),
)
.with_provider("azure-keyvault"),
}
}
#[async_trait]
impl SecretProvider for AzureSecretProvider {
async fn get_secret(&self, name: &str) -> Result<String, AdkError> {
let response = self.client.get_secret(name, "", None).await.map_err(map_azure_error)?;
let secret_bundle = response.into_body().await.map_err(|err| {
AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Internal,
"auth.azure_keyvault.response_parse",
format!("failed to parse Azure Key Vault response: {err}"),
)
.with_provider("azure-keyvault")
})?;
secret_bundle.value.ok_or_else(|| {
AdkError::new(
ErrorComponent::Auth,
ErrorCategory::Internal,
"auth.azure_keyvault.no_value",
format!("Azure Key Vault secret '{name}' exists but has no value"),
)
.with_provider("azure-keyvault")
})
}
}