1use crate::{
2 keys::is_encrypted,
3 typedetect::{FileFormat, detect_format},
4 types::{ProcessHandling, TypedValue, ValueType},
5 vaultfunc,
6};
7
8pub fn decrypt(
15 input: &str,
16 output: &str,
17 password: &str,
18 keys: &[String],
19) -> Result<(), Box<dyn std::error::Error>> {
20 let mut processor = make_decrypt_processor(password);
21 match detect_format(input)? {
22 FileFormat::Json => crate::json::process_file(input, output, keys, &mut processor),
23 FileFormat::Yaml => crate::yaml::process_file(input, output, keys, &mut processor),
24 }
25}
26
27pub(crate) fn make_decrypt_processor(
28 password: &str,
29) -> impl FnMut(
30 TypedValue,
31 ValueType,
32 &str,
33) -> Result<(TypedValue, ValueType, ProcessHandling), Box<dyn std::error::Error>>
34+ '_ {
35 move |typed, vt, key_path| {
36 let (encrypted, vault_str) = is_encrypted(&typed);
37 if !encrypted {
38 return Ok((typed, vt, ProcessHandling::Skip));
39 }
40 let vault_str = vault_str.unwrap();
41 match vaultfunc::decrypt(&vault_str, password) {
42 Ok((decrypted, new_vt)) => Ok((decrypted, new_vt, ProcessHandling::Process)),
43 Err(e) => {
44 eprintln!("error decrypting key '{}': {}", key_path, e);
45 Ok((typed, vt, ProcessHandling::Skip))
46 }
47 }
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 fn resources(filename: &str) -> String {
56 format!(
57 "{}/resources/tests/{}",
58 env!("CARGO_MANIFEST_DIR"),
59 filename
60 )
61 }
62
63 struct YamlDecryptCase {
66 input: &'static str,
67 reference: &'static str,
68 keys: &'static [&'static str],
69 }
70
71 fn run_yaml_decrypt(case: &YamlDecryptCase) {
72 let input = resources(case.input);
73 let reference = resources(case.reference);
74 let keys: Vec<String> = case.keys.iter().map(|s| s.to_string()).collect();
75
76 let output = tempfile::NamedTempFile::new().unwrap();
77 let output_path = output.path().to_str().unwrap().to_string();
78
79 decrypt(&input, &output_path, "test999", &keys).unwrap();
80
81 let got: serde_yaml::Value =
82 serde_yaml::from_str(&std::fs::read_to_string(&output_path).unwrap()).unwrap();
83 let expected: serde_yaml::Value =
84 serde_yaml::from_str(&std::fs::read_to_string(&reference).unwrap()).unwrap();
85 assert_eq!(got, expected, "YAML decrypt '{}' failed", case.input);
86 }
87
88 #[test]
89 fn test_decrypt_yaml_all_keys() {
90 run_yaml_decrypt(&YamlDecryptCase {
91 input: "partial_encrypted_example.yaml",
92 reference: "partial_encrypted_example_decrypted_01.yaml",
93 keys: &[],
94 });
95 }
96
97 #[test]
98 fn test_decrypt_yaml_filtered_key() {
99 run_yaml_decrypt(&YamlDecryptCase {
100 input: "partial_encrypted_example.yaml",
101 reference: "partial_encrypted_example_decrypted_03.yaml",
102 keys: &["third.carrot"],
103 });
104 }
105
106 #[test]
107 fn test_decrypt_yaml_multiple_keys() {
108 run_yaml_decrypt(&YamlDecryptCase {
109 input: "partial_encrypted_example.yaml",
110 reference: "partial_encrypted_example_decrypted_04.yaml",
111 keys: &["first.a", "first.z", "second.b.2", "fourth.list"],
112 });
113 }
114
115 #[test]
116 fn test_decrypt_yaml_02() {
117 run_yaml_decrypt(&YamlDecryptCase {
118 input: "partial_encrypted_example_02.yaml",
119 reference: "partial_encrypted_example_decrypted_01.yaml",
120 keys: &[],
121 });
122 }
123
124 #[test]
125 fn test_decrypt_yaml_03() {
126 run_yaml_decrypt(&YamlDecryptCase {
127 input: "partial_encrypted_example_03.yaml",
128 reference: "partial_encrypted_example_decrypted_01.yaml",
129 keys: &[],
130 });
131 }
132
133 struct JsonDecryptCase {
136 input: &'static str,
137 reference: &'static str,
138 keys: &'static [&'static str],
139 }
140
141 fn run_json_decrypt(case: &JsonDecryptCase) {
142 let input = resources(case.input);
143 let reference = resources(case.reference);
144 let keys: Vec<String> = case.keys.iter().map(|s| s.to_string()).collect();
145
146 let output = tempfile::NamedTempFile::new().unwrap();
147 let output_path = output.path().to_str().unwrap().to_string();
148
149 decrypt(&input, &output_path, "test999", &keys).unwrap();
150
151 let got: serde_json::Value =
152 serde_json::from_str(&std::fs::read_to_string(&output_path).unwrap()).unwrap();
153 let expected: serde_json::Value =
154 serde_json::from_str(&std::fs::read_to_string(&reference).unwrap()).unwrap();
155 assert_eq!(got, expected, "JSON decrypt '{}' failed", case.input);
156 }
157
158 #[test]
159 fn test_decrypt_json_all_keys() {
160 run_json_decrypt(&JsonDecryptCase {
161 input: "partial_encrypted_example.json",
162 reference: "partial_encrypted_example_decrypted_01.json",
163 keys: &[],
164 });
165 }
166
167 #[test]
168 fn test_decrypt_json_filtered_key() {
169 run_json_decrypt(&JsonDecryptCase {
170 input: "partial_encrypted_example.json",
171 reference: "partial_encrypted_example_decrypted_03.json",
172 keys: &["third.carrot"],
173 });
174 }
175
176 #[test]
177 fn test_decrypt_json_multiple_keys() {
178 run_json_decrypt(&JsonDecryptCase {
179 input: "partial_encrypted_example.json",
180 reference: "partial_encrypted_example_decrypted_04.json",
181 keys: &["first.a", "first.z", "second.b.2", "fourth.list"],
182 });
183 }
184
185 #[test]
186 fn test_decrypt_json_02() {
187 run_json_decrypt(&JsonDecryptCase {
188 input: "partial_encrypted_example_02.json",
189 reference: "partial_encrypted_example_decrypted_01.json",
190 keys: &[],
191 });
192 }
193
194 #[test]
195 fn test_decrypt_json_03() {
196 run_json_decrypt(&JsonDecryptCase {
197 input: "partial_encrypted_example_03.json",
198 reference: "partial_encrypted_example_decrypted_01.json",
199 keys: &[],
200 });
201 }
202}