1use crate::command_config::CommandConfig;
7use crate::errors::{AnsibleError, Result};
8use std::fmt::{Display, Formatter};
9use std::process;
10
11#[derive(Debug, Clone)]
81pub struct AnsibleVault {
82 pub(crate) command: String,
83 pub(crate) cfg: CommandConfig,
84 pub(crate) vault_id: Option<String>,
85 pub(crate) vault_password_file: Option<String>,
86}
87
88impl Default for AnsibleVault {
89 fn default() -> Self {
90 Self {
91 command: "ansible-vault".into(),
92 cfg: CommandConfig::default(),
93 vault_id: None,
94 vault_password_file: None,
95 }
96 }
97}
98
99impl Display for AnsibleVault {
100 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}", self.command)?;
102
103 if let Some(ref vault_id) = self.vault_id {
104 write!(f, " --vault-id {}", vault_id)?;
105 }
106
107 if let Some(ref password_file) = self.vault_password_file {
108 write!(f, " --vault-password-file {}", password_file)?;
109 }
110
111 if !self.cfg.args.is_empty() {
112 write!(f, " {}", self.cfg.args.join(" "))?;
113 }
114
115 Ok(())
116 }
117}
118
119impl AnsibleVault {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn set_vault_id(&mut self, vault_id: impl Into<String>) -> &mut Self {
127 self.vault_id = Some(vault_id.into());
128 self
129 }
130
131 pub fn set_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
133 self.vault_password_file = Some(file_path.into());
134 self
135 }
136
137 pub fn set_new_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
139 self.arg("--new-vault-password-file").arg(file_path.into());
140 self
141 }
142
143 pub fn arg(&mut self, arg: impl Into<String>) -> &mut Self {
145 self.cfg.arg(arg.into());
146 self
147 }
148
149 pub fn args<I, S>(&mut self, args: I) -> &mut Self
151 where
152 I: IntoIterator<Item = S>,
153 S: Into<String>,
154 {
155 let args_vec: Vec<String> = args.into_iter().map(|s| s.into()).collect();
156 self.cfg.args(args_vec);
157 self
158 }
159
160 pub fn set_system_envs(&mut self) -> &mut Self {
162 self.cfg.set_system_envs();
163 self
164 }
165
166 pub fn add_env(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
168 self.cfg.add_env(key, value);
169 self
170 }
171
172 fn execute_vault_command(&self, action: &str, args: &[String]) -> Result<String> {
174 let mut cmd = process::Command::new(&self.command);
175 cmd.envs(&self.cfg.envs);
176 cmd.arg(action);
177
178 if let Some(ref vault_id) = self.vault_id {
180 cmd.args(["--vault-id", vault_id]);
181 }
182
183 if let Some(ref password_file) = self.vault_password_file {
184 cmd.args(["--vault-password-file", password_file]);
185 }
186
187 cmd.args(&self.cfg.args);
189
190 cmd.args(args);
192
193 let output = cmd.output()?;
194
195 if !output.status.success() {
196 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
197 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
198 return Err(AnsibleError::command_failed(
199 format!("Ansible vault {} command failed", action),
200 output.status.code(),
201 Some(stdout),
202 Some(stderr),
203 ));
204 }
205
206 let result = [output.stdout, "\n".as_bytes().to_vec(), output.stderr].concat();
207 let s = String::from_utf8_lossy(&result);
208
209 Ok(s.to_string())
210 }
211
212 pub fn create(&self, file_path: impl Into<String>) -> Result<String> {
214 let file_path = file_path.into();
215 self.execute_vault_command("create", &[file_path])
216 }
217
218 pub fn encrypt(&self, file_path: impl Into<String>) -> Result<String> {
220 let file_path = file_path.into();
221 self.execute_vault_command("encrypt", &[file_path])
222 }
223
224 pub fn decrypt(&self, file_path: impl Into<String>) -> Result<String> {
226 let file_path = file_path.into();
227 self.execute_vault_command("decrypt", &[file_path])
228 }
229
230 pub fn view(&self, file_path: impl Into<String>) -> Result<String> {
232 let file_path = file_path.into();
233 self.execute_vault_command("view", &[file_path])
234 }
235
236 pub fn edit(&self, file_path: impl Into<String>) -> Result<String> {
238 let file_path = file_path.into();
239 self.execute_vault_command("edit", &[file_path])
240 }
241
242 pub fn rekey(&self, file_path: impl Into<String>) -> Result<String> {
244 let file_path = file_path.into();
245 self.execute_vault_command("rekey", &[file_path])
246 }
247
248 pub fn encrypt_string(&self, string_to_encrypt: impl Into<String>) -> Result<String> {
250 let string_to_encrypt = string_to_encrypt.into();
251 self.execute_vault_command("encrypt_string", &[string_to_encrypt])
252 }
253
254 pub fn encrypt_string_with_name(
256 &self,
257 string_to_encrypt: impl Into<String>,
258 var_name: impl Into<String>,
259 ) -> Result<String> {
260 let string_to_encrypt = string_to_encrypt.into();
261 let var_name = var_name.into();
262 self.execute_vault_command("encrypt_string", &[
263 "--name".to_string(),
264 var_name,
265 string_to_encrypt,
266 ])
267 }
268
269 pub fn encrypt_string_prompt(&self) -> Result<String> {
271 self.execute_vault_command("encrypt_string", &["--prompt".to_string()])
272 }
273
274 pub fn encrypt_string_stdin(&self, stdin_name: impl Into<String>) -> Result<String> {
276 let stdin_name = stdin_name.into();
277 self.execute_vault_command("encrypt_string", &[
278 "--stdin-name".to_string(),
279 stdin_name,
280 ])
281 }
282
283 pub fn decrypt_to_file(
285 &self,
286 input_file: impl Into<String>,
287 output_file: impl Into<String>,
288 ) -> Result<String> {
289 let input_file = input_file.into();
290 let output_file = output_file.into();
291 self.execute_vault_command("decrypt", &[
292 "--output".to_string(),
293 output_file,
294 input_file,
295 ])
296 }
297
298 pub fn encrypt_to_file(
300 &self,
301 input_file: impl Into<String>,
302 output_file: impl Into<String>,
303 ) -> Result<String> {
304 let input_file = input_file.into();
305 let output_file = output_file.into();
306 self.execute_vault_command("encrypt", &[
307 "--output".to_string(),
308 output_file,
309 input_file,
310 ])
311 }
312
313 pub fn set_encrypt_vault_id(&mut self, vault_id: impl Into<String>) -> &mut Self {
315 self.cfg.arg("--encrypt-vault-id");
316 self.cfg.arg(vault_id.into());
317 self
318 }
319
320 pub fn ask_vault_password(&mut self) -> &mut Self {
322 self.cfg.arg("--ask-vault-password");
323 self
324 }
325
326 pub fn verbose(&mut self) -> &mut Self {
328 self.cfg.arg("-v");
329 self
330 }
331
332 pub fn verbosity(&mut self, level: u8) -> &mut Self {
334 let v_arg = "-".to_string() + &"v".repeat(level as usize);
335 self.cfg.arg(v_arg);
336 self
337 }
338
339 pub fn get_config(&self) -> &CommandConfig {
341 &self.cfg
342 }
343}
344
345#[derive(Debug, Clone)]
368pub enum VaultError {
369 NoPassword,
374
375 InvalidFormat,
380
381 VaultIdNotFound,
386
387 DecryptionFailed,
392
393 EncryptionFailed,
398}
399
400impl std::fmt::Display for VaultError {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 match self {
403 VaultError::NoPassword => write!(f, "Vault password not provided"),
404 VaultError::InvalidFormat => write!(f, "Invalid vault file format"),
405 VaultError::VaultIdNotFound => write!(f, "Vault ID not found"),
406 VaultError::DecryptionFailed => write!(f, "Decryption failed"),
407 VaultError::EncryptionFailed => write!(f, "Encryption failed"),
408 }
409 }
410}
411
412impl std::error::Error for VaultError {}