Skip to main content

alien_core/bindings/
vault.rs

1//! Vault binding definitions for secure secret management
2//!
3//! This module defines the binding parameters for different vault services:
4//! - AWS SSM Parameter Store (SecureString)
5//! - GCP Secret Manager
6//! - Azure Key Vault
7//! - Kubernetes Secrets (native K8s secret storage)
8
9use super::BindingValue;
10use serde::{Deserialize, Serialize};
11
12/// Represents a vault binding for secure secret management
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
15#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
16#[serde(tag = "service", rename_all = "kebab-case")]
17pub enum VaultBinding {
18    /// AWS SSM Parameter Store binding (SecureString)
19    ParameterStore(ParameterStoreVaultBinding),
20    /// GCP Secret Manager binding
21    SecretManager(SecretManagerVaultBinding),
22    /// Azure Key Vault binding
23    KeyVault(KeyVaultBinding),
24    /// Kubernetes Secrets binding (native K8s secret storage)
25    KubernetesSecret(KubernetesSecretVaultBinding),
26    /// Local development vault
27    #[serde(rename = "local-vault")]
28    Local(LocalVaultBinding),
29}
30
31/// AWS SSM Parameter Store vault binding configuration
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
34#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
35#[serde(rename_all = "camelCase")]
36pub struct ParameterStoreVaultBinding {
37    /// The vault prefix for parameter names (e.g., "my-stack-my-vault")
38    pub vault_prefix: BindingValue<String>,
39}
40
41/// GCP Secret Manager vault binding configuration
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
44#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
45#[serde(rename_all = "camelCase")]
46pub struct SecretManagerVaultBinding {
47    /// The vault prefix for secret names (e.g., "my-stack-my-vault")
48    pub vault_prefix: BindingValue<String>,
49}
50
51/// Azure Key Vault binding configuration
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
54#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
55#[serde(rename_all = "camelCase")]
56pub struct KeyVaultBinding {
57    /// The Key Vault name
58    pub vault_name: BindingValue<String>,
59}
60
61/// Kubernetes Secrets vault binding configuration
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
64#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
65#[serde(rename_all = "camelCase")]
66pub struct KubernetesSecretVaultBinding {
67    /// The Kubernetes namespace where secrets are stored
68    pub namespace: BindingValue<String>,
69    /// The vault prefix for secret names (e.g., "acme-monitoring-secrets")
70    pub vault_prefix: BindingValue<String>,
71}
72
73/// Local development vault binding (for testing/development)
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
76#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
77#[serde(rename_all = "camelCase")]
78pub struct LocalVaultBinding {
79    /// The vault name for local storage
80    pub vault_name: String,
81    /// Directory where vault secrets are stored
82    pub data_dir: BindingValue<String>,
83}
84
85impl VaultBinding {
86    /// Creates an AWS SSM Parameter Store vault binding
87    pub fn parameter_store(vault_prefix: impl Into<String>) -> Self {
88        Self::ParameterStore(ParameterStoreVaultBinding {
89            vault_prefix: vault_prefix.into().into(),
90        })
91    }
92
93    /// Creates a GCP Secret Manager vault binding
94    pub fn secret_manager(vault_prefix: impl Into<String>) -> Self {
95        Self::SecretManager(SecretManagerVaultBinding {
96            vault_prefix: vault_prefix.into().into(),
97        })
98    }
99
100    /// Creates an Azure Key Vault binding
101    pub fn key_vault(vault_name: impl Into<String>) -> Self {
102        Self::KeyVault(KeyVaultBinding {
103            vault_name: vault_name.into().into(),
104        })
105    }
106
107    /// Creates a Kubernetes Secrets vault binding
108    pub fn kubernetes_secret(
109        namespace: impl Into<String>,
110        vault_prefix: impl Into<String>,
111    ) -> Self {
112        Self::KubernetesSecret(KubernetesSecretVaultBinding {
113            namespace: namespace.into().into(),
114            vault_prefix: vault_prefix.into().into(),
115        })
116    }
117
118    /// Creates a local vault binding for development/testing
119    pub fn local(vault_name: impl Into<String>, data_dir: impl Into<String>) -> Self {
120        Self::Local(LocalVaultBinding {
121            vault_name: vault_name.into(),
122            data_dir: BindingValue::value(data_dir.into()),
123        })
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_parameter_store_binding_creation() {
133        let binding = VaultBinding::parameter_store("my-vault");
134
135        match binding {
136            VaultBinding::ParameterStore(config) => {
137                assert_eq!(
138                    config.vault_prefix,
139                    BindingValue::Value("my-vault".to_string())
140                );
141            }
142            _ => panic!("Expected ParameterStore binding"),
143        }
144    }
145
146    #[test]
147    fn test_secret_manager_binding_creation() {
148        let binding = VaultBinding::secret_manager("my-vault");
149
150        match binding {
151            VaultBinding::SecretManager(config) => {
152                assert_eq!(
153                    config.vault_prefix,
154                    BindingValue::Value("my-vault".to_string())
155                );
156            }
157            _ => panic!("Expected SecretManager binding"),
158        }
159    }
160
161    #[test]
162    fn test_key_vault_binding_creation() {
163        let binding = VaultBinding::key_vault("my-key-vault");
164
165        match binding {
166            VaultBinding::KeyVault(config) => {
167                assert_eq!(
168                    config.vault_name,
169                    BindingValue::Value("my-key-vault".to_string())
170                );
171            }
172            _ => panic!("Expected KeyVault binding"),
173        }
174    }
175
176    #[test]
177    fn test_local_binding_creation() {
178        let binding = VaultBinding::local("dev-vault", "/tmp/vault-data");
179
180        match binding {
181            VaultBinding::Local(config) => {
182                assert_eq!(config.vault_name, "dev-vault");
183                assert_eq!(
184                    config.data_dir,
185                    BindingValue::Value("/tmp/vault-data".to_string())
186                );
187            }
188            _ => panic!("Expected Local binding"),
189        }
190    }
191
192    #[test]
193    fn test_serialization_roundtrip() {
194        let original = VaultBinding::parameter_store("test-vault");
195
196        let json = serde_json::to_string(&original).unwrap();
197        let deserialized: VaultBinding = serde_json::from_str(&json).unwrap();
198
199        assert_eq!(original, deserialized);
200    }
201}