ppaass_v3_common/user/repo/
fs.rs

1use crate::crypto::RsaCrypto;
2use crate::error::CommonError;
3use crate::user::{UserInfo, UserInfoRepository};
4use accessory::Accessors;
5use chrono::{DateTime, Utc};
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fs::{read_dir, File};
10use std::future::Future;
11use std::io::Read;
12use std::path::{Path, PathBuf};
13use std::sync::Arc;
14use std::time::Duration;
15use tokio::sync::RwLock;
16use tokio::time::sleep;
17use tracing::error;
18use zip::ZipArchive;
19pub const USER_INFO_ADDITION_INFO_EXPIRED_DATE_TIME: &str = "expired_date_time";
20pub const USER_INFO_ADDITION_INFO_PROXY_SERVERS: &str = "proxy_servers";
21pub const FS_USER_INFO_CONFIG_FILE_NAME: &str = "userinfo.toml";
22pub trait FsUserInfoContent {
23    fn public_key_file_relative_path(&self) -> &str;
24    fn private_key_file_relative_path(&self) -> &str;
25}
26#[derive(Debug, Serialize, Deserialize, Accessors)]
27pub struct FsAgentUserInfoContent {
28    #[access(get)]
29    public_key_file_relative_path: String,
30    #[access(get)]
31    private_key_file_relative_path: String,
32    #[access(get)]
33    proxy_servers: Vec<String>,
34}
35impl FsAgentUserInfoContent {
36    pub fn new(
37        proxy_servers: Vec<String>,
38        public_key_file_relative_path: String,
39        private_key_file_relative_path: String,
40    ) -> Self {
41        Self {
42            proxy_servers,
43            public_key_file_relative_path,
44            private_key_file_relative_path,
45        }
46    }
47}
48impl FsUserInfoContent for FsAgentUserInfoContent {
49    fn public_key_file_relative_path(&self) -> &str {
50        &self.public_key_file_relative_path
51    }
52    fn private_key_file_relative_path(&self) -> &str {
53        &self.private_key_file_relative_path
54    }
55}
56#[derive(Debug, Serialize, Deserialize, Accessors)]
57pub struct FsProxyUserInfoContent {
58    #[access(get)]
59    expired_date_time: Option<DateTime<Utc>>,
60    #[access(get)]
61    public_key_file_relative_path: String,
62    #[access(get)]
63    private_key_file_relative_path: String,
64}
65impl FsProxyUserInfoContent {
66    pub fn new(
67        expired_date_time: Option<DateTime<Utc>>,
68        public_key_file_relative_path: String,
69        private_key_file_relative_path: String,
70    ) -> Self {
71        Self {
72            expired_date_time,
73            public_key_file_relative_path,
74            private_key_file_relative_path,
75        }
76    }
77}
78impl FsUserInfoContent for FsProxyUserInfoContent {
79    fn public_key_file_relative_path(&self) -> &str {
80        &self.public_key_file_relative_path
81    }
82    fn private_key_file_relative_path(&self) -> &str {
83        &self.private_key_file_relative_path
84    }
85}
86#[derive(Debug)]
87pub struct FileSystemUserInfoRepository {
88    user_info_storage: Arc<RwLock<HashMap<String, Arc<RwLock<UserInfo>>>>>,
89}
90impl FileSystemUserInfoRepository {
91    pub async fn new<T, F, Fut>(
92        refresh_interval: u64,
93        user_repo_dir_path: &Path,
94        prepare_additional_info: F,
95    ) -> Result<Self, CommonError>
96    where
97        T: FsUserInfoContent + DeserializeOwned + Send + Sync + 'static,
98        Fut: Future<Output = ()> + Send + Sync + 'static,
99        F: Fn(Arc<RwLock<UserInfo>>, T) -> Fut + Clone + Send + Sync + 'static,
100    {
101        let user_info_storage = Arc::new(RwLock::new(HashMap::new()));
102        {
103            let user_info_storage = user_info_storage.clone();
104            let user_repo_dir_path = user_repo_dir_path.to_owned();
105            if let Err(e) = Self::fill_repo_storage(
106                prepare_additional_info.clone(),
107                user_info_storage.clone(),
108                &user_repo_dir_path,
109            )
110            .await
111            {
112                error!("Fail to build user repo:{e:?}");
113            }
114            tokio::spawn(async move {
115                loop {
116                    if let Err(e) = Self::fill_repo_storage(
117                        prepare_additional_info.clone(),
118                        user_info_storage.clone(),
119                        &user_repo_dir_path,
120                    )
121                    .await
122                    {
123                        error!("Fail to build user repo:{e:?}");
124                    }
125                    sleep(Duration::from_secs(refresh_interval)).await;
126                }
127            });
128        }
129
130        Ok(Self { user_info_storage })
131    }
132    async fn fill_repo_storage<F, T, Fut>(
133        prepare_additional_info: F,
134        user_info_storage: Arc<RwLock<HashMap<String, Arc<RwLock<UserInfo>>>>>,
135        user_repo_dir_path: &PathBuf,
136    ) -> Result<(), CommonError>
137    where
138        T: FsUserInfoContent + DeserializeOwned + Send + Sync + 'static,
139        Fut: Future<Output = ()> + Send + Sync + 'static,
140        F: Fn(Arc<RwLock<UserInfo>>, T) -> Fut + Clone + Send + Sync + 'static,
141    {
142        let user_info_dir = read_dir(&user_repo_dir_path)?;
143        for entry in user_info_dir.into_iter() {
144            let Ok(entry) = entry else {
145                error!("Fail to read user info directory [{user_repo_dir_path:?}]");
146                continue;
147            };
148            let file_name = entry.file_name();
149            let file_name = file_name.to_str();
150            let Some(file_name) = file_name else {
151                error!("Fail to read [{user_repo_dir_path:?}{file_name:?}].",);
152                continue;
153            };
154            let file_name_parts = file_name.split('.').collect::<Vec<&str>>();
155            if file_name_parts.len() < 2 {
156                error!(
157                    "Fail to read [{user_repo_dir_path:?}{file_name:?}] because of the file name is not in 2 parts",
158                );
159                continue;
160            }
161            let username = file_name_parts[0];
162            let user_zip_file_path = user_repo_dir_path.join(format!("{}.zip", username));
163            let user_zip_file = match File::open(user_zip_file_path) {
164                Ok(user_zip_file) => user_zip_file,
165                Err(e) => {
166                    error!("Fail to read user zip file: {e:?}");
167                    continue;
168                }
169            };
170            let mut user_zip_archive = match ZipArchive::new(user_zip_file) {
171                Ok(user_zip_archive) => user_zip_archive,
172                Err(e) => {
173                    error!("Fail to read user zip archive: {e:?}");
174                    continue;
175                }
176            };
177            let mut user_info_config_file_content = String::new();
178            {
179                let mut user_info_zip_file =
180                    match user_zip_archive.by_name(FS_USER_INFO_CONFIG_FILE_NAME) {
181                        Ok(user_info_file) => user_info_file,
182                        Err(e) => {
183                            error!("Fail to read user info file from zip archive: {e:?}");
184                            continue;
185                        }
186                    };
187                if let Err(e) =
188                    user_info_zip_file.read_to_string(&mut user_info_config_file_content)
189                {
190                    error!("Fail to read user info file content from zip archive: {e:?}");
191                    continue;
192                };
193            }
194            let user_info_content = match toml::from_str::<T>(&user_info_config_file_content) {
195                Ok(config) => config,
196                Err(e) => {
197                    error!("Fail to parse user info config file [{user_repo_dir_path:?}]: {e:?}");
198                    continue;
199                }
200            };
201            let mut public_key_file_content = String::new();
202            {
203                let mut public_key_zip_file = match user_zip_archive
204                    .by_name(user_info_content.public_key_file_relative_path())
205                {
206                    Ok(user_info_file) => user_info_file,
207                    Err(e) => {
208                        error!("Fail to read public key file from zip archive: {e:?}");
209                        continue;
210                    }
211                };
212                if let Err(e) = public_key_zip_file.read_to_string(&mut public_key_file_content) {
213                    error!("Fail to read public key file content from zip archive: {e:?}");
214                    continue;
215                };
216            }
217            let mut private_key_file_content = String::new();
218            {
219                let mut private_key_zip_file = match user_zip_archive
220                    .by_name(user_info_content.private_key_file_relative_path())
221                {
222                    Ok(user_info_file) => user_info_file,
223                    Err(e) => {
224                        error!("Fail to read private key file from zip archive: {e:?}");
225                        continue;
226                    }
227                };
228                if let Err(e) = private_key_zip_file.read_to_string(&mut private_key_file_content) {
229                    error!("Fail to read private key file content from zip archive: {e:?}");
230                    continue;
231                };
232            }
233            let Ok(rsa_crypto) = RsaCrypto::new(public_key_file_content, private_key_file_content)
234            else {
235                error!("Fail to create agent_user crypto for user: {username}.");
236                continue;
237            };
238            let user_info = Arc::new(RwLock::new(UserInfo::new(rsa_crypto)));
239            prepare_additional_info(user_info.clone(), user_info_content).await;
240            let mut user_info_storage = user_info_storage.write().await;
241            user_info_storage.insert(username.to_string(), user_info);
242        }
243        Ok(())
244    }
245}
246
247#[async_trait::async_trait]
248impl UserInfoRepository for FileSystemUserInfoRepository {
249    async fn get_user(&self, username: &str) -> Result<Option<Arc<RwLock<UserInfo>>>, CommonError> {
250        match self.user_info_storage.read().await.get(username) {
251            None => Ok(None),
252            Some(user_info) => Ok(Some(user_info.clone())),
253        }
254    }
255    async fn list_all_users(&self) -> Result<Vec<Arc<RwLock<UserInfo>>>, CommonError> {
256        let user_info_storage = self.user_info_storage.read().await;
257        Ok(user_info_storage
258            .values()
259            .map(|user_info| user_info.clone())
260            .collect())
261    }
262}