Skip to main content

alien_bindings/providers/vault/
azure_key_vault.rs

1use crate::error::{ErrorData, Result};
2use alien_azure_clients::keyvault::{AzureKeyVaultSecretsClient, KeyVaultSecretsApi};
3use alien_azure_clients::models::secrets::SecretSetParameters;
4use alien_error::{Context, ContextError};
5use async_trait::async_trait;
6use std::sync::Arc;
7
8/// Azure Key Vault binding implementation
9#[derive(Debug)]
10pub struct AzureKeyVault {
11    client: Arc<AzureKeyVaultSecretsClient>,
12    vault_base_url: String,
13}
14
15impl AzureKeyVault {
16    /// Create a new Azure Key Vault binding
17    pub fn new(client: Arc<AzureKeyVaultSecretsClient>, vault_base_url: String) -> Self {
18        Self {
19            client,
20            vault_base_url,
21        }
22    }
23
24    /// Azure Key Vault secret names only allow alphanumerics and hyphens.
25    /// Convert underscores to hyphens for compatibility.
26    fn sanitize_secret_name(name: &str) -> String {
27        name.replace('_', "-")
28    }
29}
30
31#[async_trait]
32impl crate::traits::Binding for AzureKeyVault {}
33
34#[async_trait]
35impl crate::traits::Vault for AzureKeyVault {
36    /// Get a secret value by name
37    async fn get_secret(&self, secret_name: &str) -> Result<String> {
38        let sanitized = Self::sanitize_secret_name(secret_name);
39        let response = self
40            .client
41            .get_secret(self.vault_base_url.clone(), sanitized, None)
42            .await
43            .context(ErrorData::CloudPlatformError {
44                message: format!(
45                    "Failed to get secret '{}' from vault '{}'",
46                    secret_name, self.vault_base_url
47                ),
48                resource_id: None,
49            })?;
50
51        response.value.ok_or_else(|| {
52            alien_error::AlienError::new(ErrorData::CloudPlatformError {
53                message: format!("Secret '{}' has no value", secret_name),
54                resource_id: None,
55            })
56        })
57    }
58
59    /// Set a secret value
60    async fn set_secret(&self, secret_name: &str, value: &str) -> Result<()> {
61        let sanitized = Self::sanitize_secret_name(secret_name);
62        let parameters = SecretSetParameters {
63            value: value.to_string(),
64            content_type: None,
65            attributes: None,
66            tags: std::collections::HashMap::new(),
67        };
68
69        self.client
70            .set_secret(self.vault_base_url.clone(), sanitized, parameters)
71            .await
72            .context(ErrorData::CloudPlatformError {
73                message: format!(
74                    "Failed to set secret '{}' in vault '{}'",
75                    secret_name, self.vault_base_url
76                ),
77                resource_id: None,
78            })?;
79
80        Ok(())
81    }
82
83    /// Delete a secret
84    async fn delete_secret(&self, secret_name: &str) -> Result<()> {
85        let sanitized = Self::sanitize_secret_name(secret_name);
86        match self
87            .client
88            .delete_secret(self.vault_base_url.clone(), sanitized)
89            .await
90        {
91            Ok(_) => Ok(()),
92            Err(error)
93                if matches!(
94                    error.error,
95                    Some(alien_client_core::ErrorData::RemoteResourceNotFound { .. })
96                ) =>
97            {
98                Ok(())
99            }
100            Err(error) => Err(error.context(ErrorData::CloudPlatformError {
101                message: format!(
102                    "Failed to delete secret '{}' from vault '{}'",
103                    secret_name, self.vault_base_url
104                ),
105                resource_id: None,
106            })),
107        }
108    }
109
110    async fn list_secrets(&self) -> Result<Vec<String>> {
111        // Azure Key Vault list is GET /secrets. The alien-azure-clients
112        // KeyVaultSecretsApi wrapper does not expose it, so this fails
113        // explicitly rather than guessing.
114        Err(alien_error::AlienError::new(
115            ErrorData::OperationNotSupported {
116                operation: "vault.list_secrets".to_string(),
117                reason: "Azure Key Vault list is not exposed by the Key Vault secrets client"
118                    .to_string(),
119            },
120        ))
121    }
122}