envault_processor/
config.rs1use crate::env::key_for;
2use crate::error::ConfigError;
3use crate::file::File;
4use envault_cipher::aes::{decrypt, encrypt};
5#[allow(unused_imports)]
6use rand::{Rng, SeedableRng};
7use std::collections::HashMap;
8
9pub struct Config {
10 pub encoded: Option<File>,
11 pub decoded: Option<File>,
12}
13#[allow(dead_code)]
14impl Config {
15 fn new(encoded: Option<File>, decoded: Option<File>) -> Self {
16 Config { encoded, decoded }
17 }
18
19 pub fn load(
20 encoded_path: Option<&str>,
21 decoded_path: Option<&str>,
22 ) -> Result<Self, ConfigError> {
23 Ok(Config {
24 encoded: match encoded_path {
25 Some(path) => Some(File::load(path)?),
26 None => None,
27 },
28 decoded: match decoded_path {
29 Some(path) => Some(File::load(path)?),
30 None => None,
31 },
32 })
33 }
34
35 pub fn save(&self, encoded_path: &str) -> Result<(), ConfigError> {
36 if let Some(encoded) = &self.encoded {
37 encoded.save(encoded_path)?;
38 Ok(())
39 } else {
40 Err(ConfigError::IllegalState)
41 }
42 }
43
44 pub fn export(&self, env: &str) -> Result<String, ConfigError> {
45 let mut result = String::new();
46 if let Some(encoded) = &self.encoded {
47 for (_env, key, value) in encoded.iter() {
48 if env == _env {
49 let password = key_for(_env, key)?;
50 result.push_str(&format!("export {}={};", key, decrypt(value, &password)?));
51 }
52 }
53 }
54 Ok(result)
55 }
56
57 pub fn apply(&mut self, env: Option<String>) -> Result<(), ConfigError> {
58 #[cfg(test)]
59 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
60 #[cfg(not(test))]
61 let mut rng = rand::thread_rng();
62
63 if let Some(decoded) = &self.decoded {
64 for (_env, key, value) in decoded.iter() {
65 if let Some(env) = env.clone() {
66 if env != _env {
67 continue;
68 }
69 }
70 let password = key_for(_env, key)?;
71 let encrypted = encrypt(value, &password, &mut rng)?;
72 match &mut self.encoded {
73 Some(ref mut encoded) => {
74 encoded.set(_env, key, &encrypted);
75 }
76 None => {
77 let mut root = HashMap::new();
78 let mut child = HashMap::new();
79 child.insert(key.to_string(), encrypted);
80 root.insert(_env.to_string(), child);
81 self.encoded = Some(File::new(root));
82 }
83 }
84 }
85 }
86 Ok(())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_config() {
96 std::env::set_var("ENV_KEY__prd__a", "password");
97 let old = vec![("prd", "a", "old_value")];
98 let new = vec![("prd", "a", "new_value")];
99 let mut config = Config::new(
100 Some(File::new(to_hash_map(old))),
101 Some(File::new(to_hash_map(new))),
102 );
103 config
104 .encoded
105 .clone()
106 .unwrap()
107 .iter()
108 .for_each(|(env, key, value)| {
109 assert_eq!(env, "prd");
110 assert_eq!(key, "a");
111 assert_eq!(value, "old_value");
112 });
113 config.apply(None).unwrap();
114 config
115 .encoded
116 .unwrap()
117 .iter()
118 .for_each(|(env, key, value)| {
119 assert_eq!(env, "prd");
120 assert_eq!(key, "a");
121 assert_eq!(value, "U2FsdGVkX19Wak5BUmlqM6K9BRN7rxlC2+NUsA+Qo4k=");
122 });
123 }
124
125 #[test]
126 fn test_export() {
127 std::env::set_var("ENV_KEY__prd__a", "password");
128 let new = vec![("prd", "a", "U2FsdGVkX19Wak5BUmlqM6K9BRN7rxlC2+NUsA+Qo4k=")];
129 let config = Config::new(Some(File::new(to_hash_map(new))), None);
130 let res = config.export("prd").unwrap();
131 assert_eq!(res, "export a=new_value;");
132 }
133
134 fn to_hash_map(v: Vec<(&str, &str, &str)>) -> HashMap<String, HashMap<String, String>> {
135 let mut hm = HashMap::new();
136 for (env, key, value) in v {
137 hm.entry(env.to_string())
138 .or_insert(HashMap::new())
139 .insert(key.to_string(), value.to_string());
140 }
141 hm
142 }
143}