use crate::{
keys::is_encryptable,
typedetect::{FileFormat, detect_format},
types::{ProcessHandling, TypedValue, ValueType},
vaultfunc,
};
pub fn encrypt(
input: &str,
output: &str,
password: &str,
keys: &[String],
) -> Result<(), Box<dyn std::error::Error>> {
let mut processor = make_encrypt_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_encrypt_processor(
password: &str,
) -> impl FnMut(
TypedValue,
ValueType,
&str,
) -> Result<(TypedValue, ValueType, ProcessHandling), Box<dyn std::error::Error>>
+ '_ {
move |typed, vt, key_path| {
if !is_encryptable(vt) {
return Ok((typed, vt, ProcessHandling::Skip));
}
match vaultfunc::encrypt(&typed, password) {
Ok(encrypted) => Ok((
TypedValue::String(encrypted),
ValueType::String,
ProcessHandling::Process,
)),
Err(e) => {
eprintln!("error encrypting key '{}': {}", key_path, e);
Ok((typed, vt, ProcessHandling::Skip))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decrypt::decrypt;
fn resources(filename: &str) -> String {
format!(
"{}/resources/tests/{}",
env!("CARGO_MANIFEST_DIR"),
filename
)
}
#[test]
fn test_encrypt_decrypt_roundtrip_yaml() {
let input = resources("example.yaml");
let encrypted = tempfile::NamedTempFile::new().unwrap();
let decrypted = tempfile::NamedTempFile::new().unwrap();
encrypt(&input, encrypted.path().to_str().unwrap(), "test999", &[]).unwrap();
decrypt(
encrypted.path().to_str().unwrap(),
decrypted.path().to_str().unwrap(),
"test999",
&[],
)
.unwrap();
let got: serde_yaml::Value = serde_yaml::from_str(
&std::fs::read_to_string(decrypted.path()).unwrap(),
)
.unwrap();
let expected: serde_yaml::Value =
serde_yaml::from_str(&std::fs::read_to_string(&input).unwrap()).unwrap();
assert_eq!(got, expected, "YAML roundtrip: encrypt+decrypt doesn't match input");
}
#[test]
fn test_encrypt_decrypt_roundtrip_yaml_filtered() {
let input = resources("example.yaml");
let encrypted = tempfile::NamedTempFile::new().unwrap();
let decrypted = tempfile::NamedTempFile::new().unwrap();
let filter = vec!["third.carrot".to_string()];
encrypt(
&input,
encrypted.path().to_str().unwrap(),
"test999",
&filter,
)
.unwrap();
decrypt(
encrypted.path().to_str().unwrap(),
decrypted.path().to_str().unwrap(),
"test999",
&filter,
)
.unwrap();
let got: serde_yaml::Value = serde_yaml::from_str(
&std::fs::read_to_string(decrypted.path()).unwrap(),
)
.unwrap();
let expected: serde_yaml::Value =
serde_yaml::from_str(&std::fs::read_to_string(&input).unwrap()).unwrap();
assert_eq!(
got, expected,
"filtered YAML roundtrip: encrypt+decrypt doesn't match input"
);
}
#[test]
fn test_encrypt_decrypt_roundtrip_json() {
let input = resources("example.json");
let encrypted = tempfile::NamedTempFile::new().unwrap();
let decrypted = tempfile::NamedTempFile::new().unwrap();
encrypt(&input, encrypted.path().to_str().unwrap(), "test999", &[]).unwrap();
decrypt(
encrypted.path().to_str().unwrap(),
decrypted.path().to_str().unwrap(),
"test999",
&[],
)
.unwrap();
let got: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(decrypted.path()).unwrap(),
)
.unwrap();
let expected: serde_json::Value =
serde_json::from_str(&std::fs::read_to_string(&input).unwrap()).unwrap();
assert_eq!(got, expected, "JSON roundtrip: encrypt+decrypt doesn't match input");
}
}