use crate::command_config::CommandConfig;
use crate::errors::{AnsibleError, Result};
use std::fmt::{Display, Formatter};
use std::process;
#[derive(Debug, Clone)]
pub struct AnsibleVault {
pub(crate) command: String,
pub(crate) cfg: CommandConfig,
pub(crate) vault_id: Option<String>,
pub(crate) vault_password_file: Option<String>,
}
impl Default for AnsibleVault {
fn default() -> Self {
Self {
command: "ansible-vault".into(),
cfg: CommandConfig::default(),
vault_id: None,
vault_password_file: None,
}
}
}
impl Display for AnsibleVault {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.command)?;
if let Some(ref vault_id) = self.vault_id {
write!(f, " --vault-id {}", vault_id)?;
}
if let Some(ref password_file) = self.vault_password_file {
write!(f, " --vault-password-file {}", password_file)?;
}
if !self.cfg.args.is_empty() {
write!(f, " {}", self.cfg.args.join(" "))?;
}
Ok(())
}
}
impl AnsibleVault {
pub fn new() -> Self {
Self::default()
}
pub fn set_vault_id(&mut self, vault_id: impl Into<String>) -> &mut Self {
self.vault_id = Some(vault_id.into());
self
}
pub fn set_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
self.vault_password_file = Some(file_path.into());
self
}
pub fn set_new_vault_password_file(&mut self, file_path: impl Into<String>) -> &mut Self {
self.arg("--new-vault-password-file").arg(file_path.into());
self
}
pub fn arg(&mut self, arg: impl Into<String>) -> &mut Self {
self.cfg.arg(arg.into());
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
let args_vec: Vec<String> = args.into_iter().map(|s| s.into()).collect();
self.cfg.args(args_vec);
self
}
pub fn set_system_envs(&mut self) -> &mut Self {
self.cfg.set_system_envs();
self
}
pub fn add_env(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.cfg.add_env(key, value);
self
}
fn execute_vault_command(&self, action: &str, args: &[String]) -> Result<String> {
let mut cmd = process::Command::new(&self.command);
cmd.envs(&self.cfg.envs);
cmd.arg(action);
if let Some(ref vault_id) = self.vault_id {
cmd.args(["--vault-id", vault_id]);
}
if let Some(ref password_file) = self.vault_password_file {
cmd.args(["--vault-password-file", password_file]);
}
cmd.args(&self.cfg.args);
cmd.args(args);
let output = cmd.output()?;
if !output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
return Err(AnsibleError::command_failed(
format!("Ansible vault {} command failed", action),
output.status.code(),
Some(stdout),
Some(stderr),
));
}
let result = [output.stdout, "\n".as_bytes().to_vec(), output.stderr].concat();
let s = String::from_utf8_lossy(&result);
Ok(s.to_string())
}
pub fn create(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("create", &[file_path])
}
pub fn encrypt(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("encrypt", &[file_path])
}
pub fn decrypt(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("decrypt", &[file_path])
}
pub fn view(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("view", &[file_path])
}
pub fn edit(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("edit", &[file_path])
}
pub fn rekey(&self, file_path: impl Into<String>) -> Result<String> {
let file_path = file_path.into();
self.execute_vault_command("rekey", &[file_path])
}
pub fn encrypt_string(&self, string_to_encrypt: impl Into<String>) -> Result<String> {
let string_to_encrypt = string_to_encrypt.into();
self.execute_vault_command("encrypt_string", &[string_to_encrypt])
}
pub fn encrypt_string_with_name(
&self,
string_to_encrypt: impl Into<String>,
var_name: impl Into<String>,
) -> Result<String> {
let string_to_encrypt = string_to_encrypt.into();
let var_name = var_name.into();
self.execute_vault_command("encrypt_string", &[
"--name".to_string(),
var_name,
string_to_encrypt,
])
}
pub fn encrypt_string_prompt(&self) -> Result<String> {
self.execute_vault_command("encrypt_string", &["--prompt".to_string()])
}
pub fn encrypt_string_stdin(&self, stdin_name: impl Into<String>) -> Result<String> {
let stdin_name = stdin_name.into();
self.execute_vault_command("encrypt_string", &[
"--stdin-name".to_string(),
stdin_name,
])
}
pub fn decrypt_to_file(
&self,
input_file: impl Into<String>,
output_file: impl Into<String>,
) -> Result<String> {
let input_file = input_file.into();
let output_file = output_file.into();
self.execute_vault_command("decrypt", &[
"--output".to_string(),
output_file,
input_file,
])
}
pub fn encrypt_to_file(
&self,
input_file: impl Into<String>,
output_file: impl Into<String>,
) -> Result<String> {
let input_file = input_file.into();
let output_file = output_file.into();
self.execute_vault_command("encrypt", &[
"--output".to_string(),
output_file,
input_file,
])
}
pub fn set_encrypt_vault_id(&mut self, vault_id: impl Into<String>) -> &mut Self {
self.cfg.arg("--encrypt-vault-id");
self.cfg.arg(vault_id.into());
self
}
pub fn ask_vault_password(&mut self) -> &mut Self {
self.cfg.arg("--ask-vault-password");
self
}
pub fn verbose(&mut self) -> &mut Self {
self.cfg.arg("-v");
self
}
pub fn verbosity(&mut self, level: u8) -> &mut Self {
let v_arg = "-".to_string() + &"v".repeat(level as usize);
self.cfg.arg(v_arg);
self
}
pub fn get_config(&self) -> &CommandConfig {
&self.cfg
}
}
#[derive(Debug, Clone)]
pub enum VaultError {
NoPassword,
InvalidFormat,
VaultIdNotFound,
DecryptionFailed,
EncryptionFailed,
}
impl std::fmt::Display for VaultError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
VaultError::NoPassword => write!(f, "Vault password not provided"),
VaultError::InvalidFormat => write!(f, "Invalid vault file format"),
VaultError::VaultIdNotFound => write!(f, "Vault ID not found"),
VaultError::DecryptionFailed => write!(f, "Decryption failed"),
VaultError::EncryptionFailed => write!(f, "Encryption failed"),
}
}
}
impl std::error::Error for VaultError {}