1use std::collections::HashMap;
2
3use crate::query::QueryUserMenusParams;
4use 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 rsa_decrypt_by_private_key(&t)
108 }
110 None => None,
111 };
112
113 if decodepassword != md5password {
114 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 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 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 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, ¶m, &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}