use crate::{
keys::is_encrypted,
typedetect::{FileFormat, detect_format},
types::{ProcessHandling, TypedValue, ValueType},
vaultfunc,
};
pub fn decrypt(
input: &str,
output: &str,
password: &str,
keys: &[String],
) -> Result<(), Box<dyn std::error::Error>> {
let mut processor = make_decrypt_processor(password);
match detect_format(input)? {
FileFormat::Json => crate::json::process_file(input, output, keys, &mut processor),
FileFormat::Yaml => crate::yaml::process_file(input, output, keys, &mut processor),
}
}
pub(crate) fn make_decrypt_processor(
password: &str,
) -> impl FnMut(
TypedValue,
ValueType,
&str,
) -> Result<(TypedValue, ValueType, ProcessHandling), Box<dyn std::error::Error>>
+ '_ {
move |typed, vt, key_path| {
let (encrypted, vault_str) = is_encrypted(&typed);
if !encrypted {
return Ok((typed, vt, ProcessHandling::Skip));
}
let vault_str = vault_str.unwrap();
match vaultfunc::decrypt(&vault_str, password) {
Ok((decrypted, new_vt)) => Ok((decrypted, new_vt, ProcessHandling::Process)),
Err(e) => {
eprintln!("error decrypting key '{}': {}", key_path, e);
Ok((typed, vt, ProcessHandling::Skip))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn resources(filename: &str) -> String {
format!(
"{}/resources/tests/{}",
env!("CARGO_MANIFEST_DIR"),
filename
)
}
struct YamlDecryptCase {
input: &'static str,
reference: &'static str,
keys: &'static [&'static str],
}
fn run_yaml_decrypt(case: &YamlDecryptCase) {
let input = resources(case.input);
let reference = resources(case.reference);
let keys: Vec<String> = case.keys.iter().map(|s| s.to_string()).collect();
let output = tempfile::NamedTempFile::new().unwrap();
let output_path = output.path().to_str().unwrap().to_string();
decrypt(&input, &output_path, "test999", &keys).unwrap();
let got: serde_yaml::Value =
serde_yaml::from_str(&std::fs::read_to_string(&output_path).unwrap()).unwrap();
let expected: serde_yaml::Value =
serde_yaml::from_str(&std::fs::read_to_string(&reference).unwrap()).unwrap();
assert_eq!(got, expected, "YAML decrypt '{}' failed", case.input);
}
#[test]
fn test_decrypt_yaml_all_keys() {
run_yaml_decrypt(&YamlDecryptCase {
input: "partial_encrypted_example.yaml",
reference: "partial_encrypted_example_decrypted_01.yaml",
keys: &[],
});
}
#[test]
fn test_decrypt_yaml_filtered_key() {
run_yaml_decrypt(&YamlDecryptCase {
input: "partial_encrypted_example.yaml",
reference: "partial_encrypted_example_decrypted_03.yaml",
keys: &["third.carrot"],
});
}
#[test]
fn test_decrypt_yaml_multiple_keys() {
run_yaml_decrypt(&YamlDecryptCase {
input: "partial_encrypted_example.yaml",
reference: "partial_encrypted_example_decrypted_04.yaml",
keys: &["first.a", "first.z", "second.b.2", "fourth.list"],
});
}
#[test]
fn test_decrypt_yaml_02() {
run_yaml_decrypt(&YamlDecryptCase {
input: "partial_encrypted_example_02.yaml",
reference: "partial_encrypted_example_decrypted_01.yaml",
keys: &[],
});
}
#[test]
fn test_decrypt_yaml_03() {
run_yaml_decrypt(&YamlDecryptCase {
input: "partial_encrypted_example_03.yaml",
reference: "partial_encrypted_example_decrypted_01.yaml",
keys: &[],
});
}
struct JsonDecryptCase {
input: &'static str,
reference: &'static str,
keys: &'static [&'static str],
}
fn run_json_decrypt(case: &JsonDecryptCase) {
let input = resources(case.input);
let reference = resources(case.reference);
let keys: Vec<String> = case.keys.iter().map(|s| s.to_string()).collect();
let output = tempfile::NamedTempFile::new().unwrap();
let output_path = output.path().to_str().unwrap().to_string();
decrypt(&input, &output_path, "test999", &keys).unwrap();
let got: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&output_path).unwrap()).unwrap();
let expected: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&reference).unwrap()).unwrap();
assert_eq!(got, expected, "JSON decrypt '{}' failed", case.input);
}
#[test]
fn test_decrypt_json_all_keys() {
run_json_decrypt(&JsonDecryptCase {
input: "partial_encrypted_example.json",
reference: "partial_encrypted_example_decrypted_01.json",
keys: &[],
});
}
#[test]
fn test_decrypt_json_filtered_key() {
run_json_decrypt(&JsonDecryptCase {
input: "partial_encrypted_example.json",
reference: "partial_encrypted_example_decrypted_03.json",
keys: &["third.carrot"],
});
}
#[test]
fn test_decrypt_json_multiple_keys() {
run_json_decrypt(&JsonDecryptCase {
input: "partial_encrypted_example.json",
reference: "partial_encrypted_example_decrypted_04.json",
keys: &["first.a", "first.z", "second.b.2", "fourth.list"],
});
}
#[test]
fn test_decrypt_json_02() {
run_json_decrypt(&JsonDecryptCase {
input: "partial_encrypted_example_02.json",
reference: "partial_encrypted_example_decrypted_01.json",
keys: &[],
});
}
#[test]
fn test_decrypt_json_03() {
run_json_decrypt(&JsonDecryptCase {
input: "partial_encrypted_example_03.json",
reference: "partial_encrypted_example_decrypted_01.json",
keys: &[],
});
}
}