chimes_rust/handler/
user_login.rs

1use std::collections::HashMap;
2
3use crate::query::QueryUserMenusParams;
4/**
5 * Generate the file for query_user.rs,
6 */
7use crate::{
8    entity::{ChimesUserDetailInfo, ChimesUserInfo},
9    query::QueryUserMenus,
10    utils::{generate_rand_string, get_local_timestamp, SystemUser, UserClaims},
11};
12use actix_web::{web, HttpRequest, HttpResponse, Result};
13use base64::{engine::general_purpose, Engine as _};
14use chimes_auth::{ApiResult, ChimesAuthUser};
15use chimes_utils::{
16    get_rbatis, global_app_data_get, global_app_data_insert, global_app_data_remove,
17    rsa_decrypt_by_private_key, rsa_encrypt_by_public_key,
18};
19
20use serde::{Deserialize, Serialize};
21
22#[get("/api/v1/auth/code")]
23pub async fn auth_code(_req: HttpRequest) -> Result<HttpResponse> {
24    let png = captcha::Captcha::new()
25        .add_chars(5)
26        .apply_filter(captcha::filters::Noise::new(0.4))
27        .view(180, 80)
28        .as_tuple();
29    match png {
30        Some(st) => {
31            let basestr = general_purpose::STANDARD_NO_PAD.encode(st.1);
32            let keyid = generate_rand_string(18);
33            global_app_data_insert(&keyid.clone(), &st.0);
34            let ret: web::Json<ApiResult<String>> =
35                web::Json(ApiResult::new(200, &keyid, basestr, get_local_timestamp()));
36            Ok(HttpResponse::Ok().json(ret))
37        }
38        None => {
39            let ret: web::Json<ApiResult<String>> =
40                web::Json(ApiResult::error(5010, &"FAILED".to_string()));
41            Ok(HttpResponse::Ok().json(ret))
42        }
43    }
44}
45
46#[derive(Debug, Clone, Default, Deserialize, Serialize)]
47pub struct UserAuth {
48    pub company_code: Option<String>,
49    pub username: String,
50    pub password: String,
51    pub verify_code_key: String,
52    pub verify_code: String,
53    pub open_id: Option<String>,
54    pub force_change: Option<bool>,
55    pub remember: Option<bool>,
56}
57
58#[derive(Debug, Clone, Default, Deserialize, Serialize)]
59pub struct UserResponse {
60    pub token: String,
61    pub username: String,
62    pub company_code: Option<String>,
63    pub authorities: Vec<HashMap<String, String>>,
64    pub detail: Option<ChimesUserDetailInfo>,
65    pub roles: Vec<String>,
66}
67
68#[post("/api/v1/auth/login")]
69pub async fn auth_login(req: web::Json<UserAuth>) -> Result<HttpResponse> {
70    let rb = get_rbatis();
71    let auth = req.to_owned();
72
73    let codeval = global_app_data_get(&auth.verify_code_key);
74    if codeval.is_none() {
75        let ret: web::Json<ApiResult<String>> =
76            web::Json(ApiResult::error(5301, &"VERIFY-CODE-KEY".to_string()));
77        return Ok(HttpResponse::Ok().json(ret));
78    }
79
80    global_app_data_remove(&auth.verify_code_key);
81
82    log::info!(
83        "CompanyCode: {}, Username: {}",
84        auth.company_code.clone().unwrap_or_default(),
85        auth.username.clone()
86    );
87
88    if codeval.unwrap().to_lowercase() != auth.verify_code.to_lowercase() {
89        let ret: web::Json<ApiResult<String>> =
90            web::Json(ApiResult::error(5301, &"VERIFY-CODE".to_string()));
91        return Ok(HttpResponse::Ok().json(ret));
92    }
93
94    match ChimesUserDetailInfo::load_username(rb, &auth.username, &auth.company_code).await {
95        Ok(st) => {
96            match st {
97                Some(us) => {
98                    let decodepassword = rsa_decrypt_by_private_key(&auth.password);
99                    if decodepassword.is_some() {
100                        let pwd =
101                            rsa_encrypt_by_public_key(&decodepassword.clone().unwrap_or_default());
102                        log::warn!("Re-encrypt password: {}", pwd.unwrap_or_default());
103                    }
104                    let md5password = match us.password.clone() {
105                        Some(t) => {
106                            // let digist = md5::compute(t);
107                            rsa_decrypt_by_private_key(&t)
108                            // Some(format!("{:x}", digist))
109                        }
110                        None => None,
111                    };
112
113                    if decodepassword != md5password {
114                        // may need to encode the password
115                        let ret: web::Json<ApiResult<String>> =
116                            web::Json(ApiResult::error(5302, &"PASSWORD".to_string()));
117                        Ok(HttpResponse::Ok().json(ret))
118                    } else {
119                        if auth.open_id.is_some() {
120                            if let Ok(mt) = ChimesUserDetailInfo::load_openid(
121                                rb,
122                                &auth.open_id.clone().unwrap_or_default(),
123                            )
124                            .await
125                            {
126                                if let Some(ut) = mt {
127                                    if ut.user_id != us.user_id && auth.force_change != Some(true) {
128                                        let ret: web::Json<ApiResult<String>> = web::Json(
129                                            ApiResult::error(5601, &"NEED-CONFIRM".to_string()),
130                                        );
131                                        return Ok(HttpResponse::Ok().json(ret));
132                                    }
133                                    if auth.force_change == Some(true)
134                                        && auth.remember == Some(true)
135                                    {
136                                        let mut cusinfo = ut.to_user();
137                                        cusinfo.open_id = None;
138                                        // cusinfo.union_id = None;
139                                        if auth.company_code.is_some() {
140                                            let cusacc = cusinfo.to_account();
141                                            let _ = cusacc.update_openid(rb).await.is_ok();
142                                        } else {
143                                            let _ = cusinfo.update(rb).await.is_ok();
144                                        }
145                                        let mut xusinfo = us.to_user();
146                                        xusinfo.open_id = auth.open_id.clone();
147                                        // xusinfo.union_id = auth.union_id.clone();
148                                        if auth.company_code.is_some() {
149                                            let xusacc = xusinfo.to_account();
150                                            let _ = xusacc.update_openid(rb).await.is_ok();
151                                        } else {
152                                            let _ = xusinfo.update(rb).await.is_ok();
153                                        }
154                                    }
155                                } else if auth.remember == Some(true) {
156                                    let mut xusinfo = us.to_user();
157                                    xusinfo.open_id = auth.open_id.clone();
158                                    // xusinfo.union_id = auth.union_id.clone();
159                                    if auth.company_code.is_some() {
160                                        let xusacc = xusinfo.to_account();
161                                        let _ = xusacc.update_openid(rb).await.is_ok();
162                                    } else {
163                                        let _ = xusinfo.update(rb).await.is_ok();
164                                    }
165                                }
166                            }
167                        }
168
169                        let usn = if auth.company_code.is_some() {
170                            auth.company_code.clone().unwrap_or_default()
171                                + "$$"
172                                + us.username.clone().unwrap().as_str()
173                        } else {
174                            us.username.clone().unwrap()
175                        };
176
177                        let claim = UserClaims {
178                            aud: usn,
179                            sub: format!("{}", us.user_id.unwrap_or_default()),
180                            exp: get_local_timestamp() as usize,
181                        };
182                        match claim.encode() {
183                            Some(token) => {
184                                let mut usc = us.clone();
185                                usc.password = None;
186                                let username = usc.username.clone().unwrap();
187                                let mut hs = HashMap::new();
188                                hs.insert("authority".to_string(), username.clone());
189                                let mut roles = vec![];
190                                roles.push(username.clone());
191                                for rl in usc.roles.clone() {
192                                    roles.push(rl.role_code.unwrap_or_default());
193                                }
194                                let umparam = QueryUserMenusParams {
195                                    username: username.clone(),
196                                    role_codes: roles.clone(),
197                                };
198
199                                if let Ok(rls) = QueryUserMenus::query(rb, &umparam).await {
200                                    rls.into_iter().for_each(|f| {
201                                        if f.permission.is_some() {
202                                            roles.push(f.permission.unwrap_or_default());
203                                        }
204                                    });
205                                };
206                                let up = UserResponse {
207                                    username: username.clone(),
208                                    company_code: auth.company_code.clone(),
209                                    authorities: vec![hs],
210                                    token,
211                                    roles,
212                                    detail: Some(usc),
213                                };
214
215                                let ret: web::Json<ApiResult<UserResponse>> =
216                                    web::Json(ApiResult::ok(up));
217                                Ok(HttpResponse::Ok().json(ret))
218                            }
219                            None => {
220                                let ret: web::Json<ApiResult<UserResponse>> = web::Json(
221                                    ApiResult::error(5404, &"TOKEN was not generated.".to_string()),
222                                );
223                                Ok(HttpResponse::Ok().json(ret))
224                            }
225                        }
226                    }
227                }
228                None => {
229                    let ret: web::Json<ApiResult<UserResponse>> = web::Json(ApiResult::error(
230                        5404,
231                        &format!("{} is not found.", auth.username),
232                    ));
233                    Ok(HttpResponse::Ok().json(ret))
234                }
235            }
236        }
237        Err(err) => {
238            let ret: web::Json<ApiResult<UserResponse>> =
239                web::Json(ApiResult::error(5010, &err.to_string()));
240            Ok(HttpResponse::Ok().json(ret))
241        }
242    }
243}
244
245#[post("/api/v1/auth/logout")]
246pub async fn auth_logout() -> Result<HttpResponse> {
247    let ret: web::Json<ApiResult<UserResponse>> =
248        web::Json(ApiResult::error(200, &"OK".to_string()));
249    Ok(HttpResponse::Ok().json(ret))
250}
251
252#[get("/api/v1/healthcheck")]
253pub async fn healthcheck() -> Result<HttpResponse> {
254    let ret: web::Json<ApiResult<String>> = web::Json(ApiResult::ok("Iamliving".to_string()));
255    Ok(HttpResponse::Ok().json(ret))
256}
257
258#[get("/api/v1/auth/info")]
259pub async fn auth_info(su: SystemUser<ChimesUserInfo>) -> Result<HttpResponse> {
260    let rb = get_rbatis();
261    let param = su.get_user_name();
262    let company_code = su.user.company_code.clone();
263
264    match ChimesUserDetailInfo::load_username(rb, &param, &company_code.clone()).await {
265        Ok(st) => match st {
266            Some(us) => {
267                let mut mutus = us;
268                mutus.password = None;
269
270                let claim = UserClaims {
271                    aud: mutus.username.clone().unwrap(),
272                    sub: format!("{}", mutus.user_id.unwrap_or_default()),
273                    exp: get_local_timestamp() as usize,
274                };
275                match claim.encode() {
276                    Some(token) => {
277                        let username = mutus.username.clone().unwrap();
278                        let mut hs = HashMap::new();
279                        hs.insert("authority".to_string(), username.clone());
280
281                        let mut roles = vec![];
282                        roles.push(username.clone());
283                        for rl in mutus.roles.clone() {
284                            roles.push(rl.role_code.unwrap_or_default());
285                        }
286                        let umparam = QueryUserMenusParams {
287                            username: username.clone(),
288                            role_codes: roles.clone(),
289                        };
290
291                        if let Ok(rls) = QueryUserMenus::query(rb, &umparam).await {
292                            rls.into_iter().for_each(|f| {
293                                if f.permission.is_some() {
294                                    roles.push(f.permission.unwrap_or_default());
295                                }
296                            });
297                        };
298
299                        let up = UserResponse {
300                            username: username.clone(),
301                            company_code: company_code.clone(),
302                            authorities: vec![hs],
303                            token,
304                            roles,
305                            detail: Some(mutus),
306                        };
307                        let ret: web::Json<ApiResult<UserResponse>> = web::Json(ApiResult::ok(up));
308                        Ok(HttpResponse::Ok().json(ret))
309                    }
310                    None => {
311                        let ret: web::Json<ApiResult<UserResponse>> = web::Json(ApiResult::error(
312                            5404,
313                            &"TOKEN was not generated.".to_string(),
314                        ));
315                        Ok(HttpResponse::Ok().json(ret))
316                    }
317                }
318            }
319            None => {
320                let ret: web::Json<ApiResult<UserResponse>> =
321                    web::Json(ApiResult::error(5404, &format!("{} is not found.", param)));
322                Ok(HttpResponse::Ok().json(ret))
323            }
324        },
325        Err(err) => {
326            let ret: web::Json<ApiResult<UserResponse>> =
327                web::Json(ApiResult::error(5010, &err.to_string()));
328            Ok(HttpResponse::Ok().json(ret))
329        }
330    }
331}