alien_bindings/providers/vault/
local.rs1use crate::error::{ErrorData, Result};
2use alien_error::{AlienError, Context, IntoAlienError};
3use async_trait::async_trait;
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7#[derive(Debug)]
12pub struct LocalVault {
13 vault_name: String,
14 vault_dir: PathBuf,
15}
16
17impl LocalVault {
18 pub fn new(vault_name: String, vault_dir: PathBuf) -> Self {
24 Self {
25 vault_name,
26 vault_dir,
27 }
28 }
29
30 fn secrets_file_path(&self) -> PathBuf {
32 self.vault_dir.join("secrets.json")
33 }
34
35 async fn load_secrets(&self) -> Result<HashMap<String, String>> {
37 let secrets_file = self.secrets_file_path();
38
39 if !secrets_file.exists() {
40 return Ok(HashMap::new());
41 }
42
43 let content = tokio::fs::read_to_string(&secrets_file)
44 .await
45 .into_alien_error()
46 .context(ErrorData::CloudPlatformError {
47 message: format!(
48 "Failed to read vault secrets file: {}",
49 secrets_file.display()
50 ),
51 resource_id: None,
52 })?;
53
54 serde_json::from_str(&content)
55 .into_alien_error()
56 .context(ErrorData::CloudPlatformError {
57 message: format!(
58 "Failed to parse vault secrets file: {}",
59 secrets_file.display()
60 ),
61 resource_id: None,
62 })
63 }
64
65 async fn save_secrets(&self, secrets: &HashMap<String, String>) -> Result<()> {
67 let secrets_file = self.secrets_file_path();
68
69 if let Some(parent) = secrets_file.parent() {
71 tokio::fs::create_dir_all(parent)
72 .await
73 .into_alien_error()
74 .context(ErrorData::CloudPlatformError {
75 message: format!("Failed to create vault directory: {}", parent.display()),
76 resource_id: None,
77 })?;
78 }
79
80 let json = serde_json::to_string_pretty(secrets)
81 .into_alien_error()
82 .context(ErrorData::CloudPlatformError {
83 message: "Failed to serialize vault secrets".to_string(),
84 resource_id: None,
85 })?;
86
87 let path = secrets_file.clone();
88 let data = json.into_bytes();
89 tokio::task::spawn_blocking(move || {
90 alien_core::file_utils::write_secret_file(&path, &data)
91 })
92 .await
93 .into_alien_error()
94 .context(ErrorData::CloudPlatformError {
95 message: "Failed to spawn blocking write task".to_string(),
96 resource_id: None,
97 })?
98 .into_alien_error()
99 .context(ErrorData::CloudPlatformError {
100 message: format!(
101 "Failed to write vault secrets file: {}",
102 secrets_file.display()
103 ),
104 resource_id: None,
105 })
106 }
107}
108
109#[async_trait]
110impl crate::traits::Binding for LocalVault {}
111
112#[async_trait]
113impl crate::traits::Vault for LocalVault {
114 async fn get_secret(&self, secret_name: &str) -> Result<String> {
116 let secrets = self.load_secrets().await?;
117
118 secrets.get(secret_name).cloned().ok_or_else(|| {
119 AlienError::new(ErrorData::CloudPlatformError {
120 message: format!(
121 "Secret '{}' not found in vault '{}'",
122 secret_name, self.vault_name
123 ),
124 resource_id: None,
125 })
126 })
127 }
128
129 async fn set_secret(&self, secret_name: &str, value: &str) -> Result<()> {
131 let mut secrets = self.load_secrets().await?;
132 secrets.insert(secret_name.to_string(), value.to_string());
133 self.save_secrets(&secrets).await
134 }
135
136 async fn delete_secret(&self, secret_name: &str) -> Result<()> {
138 let mut secrets = self.load_secrets().await?;
139
140 secrets.remove(secret_name).ok_or_else(|| {
141 AlienError::new(ErrorData::CloudPlatformError {
142 message: format!(
143 "Secret '{}' not found in vault '{}'",
144 secret_name, self.vault_name
145 ),
146 resource_id: None,
147 })
148 })?;
149
150 self.save_secrets(&secrets).await
151 }
152}