1use crate::api::plugin::{ConfigFilter, ConfigReq, ConfigResp};
2
3pub const DEFAULT_CIPHER_PREFIX: &str = "cipher-";
7
8pub const DEFAULT_CIPHER_SPLIT: &str = "-";
9
10#[async_trait::async_trait]
12pub trait EncryptionPlugin: Send + Sync {
13 fn need_cipher(&self, data_id: &str) -> bool {
19 data_id.starts_with(DEFAULT_CIPHER_PREFIX)
20 && self
21 .parse_algorithm_name(data_id)
22 .eq(&self.algorithm_name())
23 }
24
25 fn parse_algorithm_name(&self, data_id: &str) -> String {
32 data_id
33 .split(DEFAULT_CIPHER_SPLIT)
34 .nth(1)
35 .unwrap()
36 .to_string()
37 }
38
39 async fn encrypt(&self, secret_key: &str, content: &str) -> String;
47
48 async fn decrypt(&self, secret_key: &str, content: &str) -> String;
56
57 async fn generate_secret_key(&self) -> String;
63
64 fn algorithm_name(&self) -> String;
70
71 async fn encrypt_secret_key(&self, secret_key: &str) -> String;
78
79 async fn decrypt_secret_key(&self, secret_key: &str) -> String;
86}
87
88pub struct ConfigEncryptionFilter {
90 encryption_plugins: Vec<Box<dyn EncryptionPlugin>>,
91}
92
93impl ConfigEncryptionFilter {
94 pub fn new(encryption_plugins: Vec<Box<dyn EncryptionPlugin>>) -> Self {
95 Self { encryption_plugins }
96 }
97}
98
99#[async_trait::async_trait]
100impl ConfigFilter for ConfigEncryptionFilter {
101 async fn filter(
102 &self,
103 config_req: Option<&mut ConfigReq>,
104 config_resp: Option<&mut ConfigResp>,
105 ) {
106 if let Some(config_req) = config_req {
108 for plugin in &self.encryption_plugins {
109 if !plugin.need_cipher(&config_req.data_id) {
110 continue;
111 }
112
113 let secret_key = plugin.generate_secret_key().await;
114 let encrypted_content = plugin.encrypt(&secret_key, &config_req.content).await;
115 let encrypted_secret_key = plugin.encrypt_secret_key(&secret_key).await;
116
117 config_req.encrypted_data_key = encrypted_secret_key;
119 config_req.content = encrypted_content;
120 break;
121 }
122 }
123
124 if let Some(config_resp) = config_resp {
126 if !config_resp.encrypted_data_key.is_empty() {
127 for plugin in &self.encryption_plugins {
128 if !plugin.need_cipher(&config_resp.data_id) {
129 continue;
130 }
131
132 let encrypted_secret_key = &config_resp.encrypted_data_key;
134 let encrypted_content = &config_resp.content;
135
136 let decrypted_secret_key =
137 plugin.decrypt_secret_key(encrypted_secret_key).await;
138 let decrypted_content = plugin
139 .decrypt(&decrypted_secret_key, encrypted_content)
140 .await;
141
142 config_resp.content = decrypted_content;
144 break;
145 }
146 }
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::api::plugin::config_filter::{ConfigReq, ConfigResp};
154 use crate::api::plugin::encryption::DEFAULT_CIPHER_PREFIX;
155 use crate::api::plugin::{ConfigEncryptionFilter, ConfigFilter, EncryptionPlugin};
156
157 struct TestEncryptionPlugin;
158
159 #[async_trait::async_trait]
160 impl EncryptionPlugin for TestEncryptionPlugin {
161 async fn encrypt(&self, secret_key: &str, content: &str) -> String {
162 secret_key.to_owned() + content
163 }
164
165 async fn decrypt(&self, secret_key: &str, content: &str) -> String {
166 content.replace(secret_key, "")
167 }
168
169 async fn generate_secret_key(&self) -> String {
170 "secret-key".to_string()
171 }
172
173 fn algorithm_name(&self) -> String {
174 "TEST".to_string()
175 }
176
177 async fn encrypt_secret_key(&self, secret_key: &str) -> String {
178 "crypt_".to_owned() + secret_key
179 }
180
181 async fn decrypt_secret_key(&self, secret_key: &str) -> String {
182 secret_key.replace("crypt_", "")
183 }
184 }
185
186 #[tokio::test]
187 async fn test_config_encryption_filters_empty() {
188 let config_encryption_filter = ConfigEncryptionFilter::new(vec![]);
189
190 let (data_id, group, namespace, content, encrypted_data_key) = (
191 "D".to_string(),
192 "G".to_string(),
193 "N".to_string(),
194 "C".to_string(),
195 "".to_string(),
196 );
197
198 let mut config_req = ConfigReq::new(
199 data_id.clone(),
200 group.clone(),
201 namespace.clone(),
202 content.clone(),
203 encrypted_data_key.clone(),
204 );
205 config_encryption_filter
206 .filter(Some(&mut config_req), None)
207 .await;
208
209 assert_eq!(config_req.content, encrypted_data_key + content.as_str());
210
211 let mut config_resp = ConfigResp::new(
212 config_req.data_id.clone(),
213 config_req.group.clone(),
214 config_req.namespace.clone(),
215 config_req.content.clone(),
216 config_req.encrypted_data_key.clone(),
217 );
218 config_encryption_filter
219 .filter(None, Some(&mut config_resp))
220 .await;
221
222 assert_eq!(config_resp.content, content);
223 }
224
225 #[tokio::test]
226 async fn test_config_encryption_filters() {
227 let config_encryption_filter =
228 ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
229
230 let (data_id, group, namespace, content, encrypted_data_key) = (
231 DEFAULT_CIPHER_PREFIX.to_owned() + "-TEST-D",
232 "G".to_string(),
233 "N".to_string(),
234 "C".to_string(),
235 "E".to_string(),
236 );
237
238 let mut config_req = ConfigReq::new(
239 data_id.clone(),
240 group.clone(),
241 namespace.clone(),
242 content.clone(),
243 encrypted_data_key.clone(),
244 );
245 config_encryption_filter
246 .filter(Some(&mut config_req), None)
247 .await;
248
249 let mut config_resp = ConfigResp::new(
250 config_req.data_id.clone(),
251 config_req.group.clone(),
252 config_req.namespace.clone(),
253 config_req.content.clone(),
254 config_req.encrypted_data_key.clone(),
255 );
256 config_encryption_filter
257 .filter(None, Some(&mut config_resp))
258 .await;
259
260 assert_eq!(config_resp.content, content);
261 }
262
263 #[tokio::test]
264 async fn test_config_encryption_filters_not_need_cipher() {
265 let config_encryption_filter =
266 ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
267
268 let (data_id, group, namespace, content, encrypted_data_key) = (
269 "D".to_string(),
270 "G".to_string(),
271 "N".to_string(),
272 "C".to_string(),
273 "E".to_string(),
274 );
275
276 let mut config_req = ConfigReq::new(
277 data_id.clone(),
278 group.clone(),
279 namespace.clone(),
280 content.clone(),
281 encrypted_data_key.clone(),
282 );
283 config_encryption_filter
284 .filter(Some(&mut config_req), None)
285 .await;
286
287 let mut config_resp = ConfigResp::new(
288 config_req.data_id.clone(),
289 config_req.group.clone(),
290 config_req.namespace.clone(),
291 config_req.content.clone(),
292 config_req.encrypted_data_key.clone(),
293 );
294 config_encryption_filter
295 .filter(None, Some(&mut config_resp))
296 .await;
297
298 assert_eq!(config_resp.content, content);
299 }
300}