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 .expect("data_id should have algorithm name in the second segment")
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 && !config_resp.encrypted_data_key.is_empty()
127 {
128 for plugin in &self.encryption_plugins {
129 if !plugin.need_cipher(&config_resp.data_id) {
130 continue;
131 }
132
133 let encrypted_secret_key = &config_resp.encrypted_data_key;
135 let encrypted_content = &config_resp.content;
136
137 let decrypted_secret_key = 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#[cfg(test)]
151mod tests {
152 use crate::api::plugin::config_filter::{ConfigReq, ConfigResp};
153 use crate::api::plugin::encryption::DEFAULT_CIPHER_PREFIX;
154 use crate::api::plugin::{ConfigEncryptionFilter, ConfigFilter, EncryptionPlugin};
155
156 struct TestEncryptionPlugin;
157
158 #[async_trait::async_trait]
159 impl EncryptionPlugin for TestEncryptionPlugin {
160 async fn encrypt(&self, secret_key: &str, content: &str) -> String {
161 secret_key.to_owned() + content
162 }
163
164 async fn decrypt(&self, secret_key: &str, content: &str) -> String {
165 content.replace(secret_key, "")
166 }
167
168 async fn generate_secret_key(&self) -> String {
169 "secret-key".to_string()
170 }
171
172 fn algorithm_name(&self) -> String {
173 "TEST".to_string()
174 }
175
176 async fn encrypt_secret_key(&self, secret_key: &str) -> String {
177 "crypt_".to_owned() + secret_key
178 }
179
180 async fn decrypt_secret_key(&self, secret_key: &str) -> String {
181 secret_key.replace("crypt_", "")
182 }
183 }
184
185 #[tokio::test]
186 async fn test_config_encryption_filters_empty() {
187 let config_encryption_filter = ConfigEncryptionFilter::new(vec![]);
188
189 let (data_id, group, namespace, content, encrypted_data_key) = (
190 "D".to_string(),
191 "G".to_string(),
192 "N".to_string(),
193 "C".to_string(),
194 "".to_string(),
195 );
196
197 let mut config_req = ConfigReq::new(
198 data_id.clone(),
199 group.clone(),
200 namespace.clone(),
201 content.clone(),
202 encrypted_data_key.clone(),
203 );
204 config_encryption_filter
205 .filter(Some(&mut config_req), None)
206 .await;
207
208 assert_eq!(config_req.content, encrypted_data_key + content.as_str());
209
210 let mut config_resp = ConfigResp::new(
211 config_req.data_id.clone(),
212 config_req.group.clone(),
213 config_req.namespace.clone(),
214 config_req.content.clone(),
215 config_req.encrypted_data_key.clone(),
216 );
217 config_encryption_filter
218 .filter(None, Some(&mut config_resp))
219 .await;
220
221 assert_eq!(config_resp.content, content);
222 }
223
224 #[tokio::test]
225 async fn test_config_encryption_filters() {
226 let config_encryption_filter =
227 ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
228
229 let (data_id, group, namespace, content, encrypted_data_key) = (
230 DEFAULT_CIPHER_PREFIX.to_owned() + "-TEST-D",
231 "G".to_string(),
232 "N".to_string(),
233 "C".to_string(),
234 "E".to_string(),
235 );
236
237 let mut config_req = ConfigReq::new(
238 data_id.clone(),
239 group.clone(),
240 namespace.clone(),
241 content.clone(),
242 encrypted_data_key.clone(),
243 );
244 config_encryption_filter
245 .filter(Some(&mut config_req), None)
246 .await;
247
248 let mut config_resp = ConfigResp::new(
249 config_req.data_id.clone(),
250 config_req.group.clone(),
251 config_req.namespace.clone(),
252 config_req.content.clone(),
253 config_req.encrypted_data_key.clone(),
254 );
255 config_encryption_filter
256 .filter(None, Some(&mut config_resp))
257 .await;
258
259 assert_eq!(config_resp.content, content);
260 }
261
262 #[tokio::test]
263 async fn test_config_encryption_filters_not_need_cipher() {
264 let config_encryption_filter =
265 ConfigEncryptionFilter::new(vec![Box::new(TestEncryptionPlugin {})]);
266
267 let (data_id, group, namespace, content, encrypted_data_key) = (
268 "D".to_string(),
269 "G".to_string(),
270 "N".to_string(),
271 "C".to_string(),
272 "E".to_string(),
273 );
274
275 let mut config_req = ConfigReq::new(
276 data_id.clone(),
277 group.clone(),
278 namespace.clone(),
279 content.clone(),
280 encrypted_data_key.clone(),
281 );
282 config_encryption_filter
283 .filter(Some(&mut config_req), None)
284 .await;
285
286 let mut config_resp = ConfigResp::new(
287 config_req.data_id.clone(),
288 config_req.group.clone(),
289 config_req.namespace.clone(),
290 config_req.content.clone(),
291 config_req.encrypted_data_key.clone(),
292 );
293 config_encryption_filter
294 .filter(None, Some(&mut config_resp))
295 .await;
296
297 assert_eq!(config_resp.content, content);
298 }
299}