1use bios_basic::rbum::dto::rbum_cert_conf_dto::{RbumCertConfAddReq, RbumCertConfModifyReq};
2use bios_basic::rbum::dto::rbum_cert_dto::{RbumCertAddReq, RbumCertModifyReq};
3use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumCertConfFilterReq, RbumCertFilterReq};
4use bios_basic::rbum::rbum_enumeration::{RbumCertConfStatusKind, RbumCertRelKind, RbumCertStatusKind};
5use bios_basic::rbum::serv::rbum_cert_serv::{RbumCertConfServ, RbumCertServ};
6use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
7use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
8use tardis::basic::dto::TardisContext;
9use tardis::basic::field::TrimString;
10use tardis::basic::result::TardisResult;
11use tardis::log::info;
12use tardis::rand::Rng;
13use tardis::TardisFunsInst;
14
15use crate::basic::dto::iam_cert_conf_dto::IamCertConfMailVCodeAddOrModifyReq;
16use crate::basic::dto::iam_cert_dto::{IamCertMailVCodeAddReq, IamCertMailVCodeModifyReq};
17use crate::basic::dto::iam_filer_dto::IamAccountFilterReq;
18use crate::basic::serv::iam_account_serv::IamAccountServ;
19use crate::basic::serv::iam_cert_serv::IamCertServ;
20use crate::basic::serv::iam_tenant_serv::IamTenantServ;
21use crate::iam_config::IamBasicConfigApi;
22use crate::iam_enumeration::IamCertKernelKind;
23
24use super::clients::iam_log_client::{IamLogClient, LogParamTag};
25use super::clients::iam_search_client::IamSearchClient;
26use super::clients::mail_client::MailClient;
27
28pub struct IamCertMailVCodeServ;
29
30impl IamCertMailVCodeServ {
31 pub async fn add_cert_conf(add_req: &IamCertConfMailVCodeAddOrModifyReq, rel_iam_item_id: Option<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
32 let id = RbumCertConfServ::add_rbum(
33 &mut RbumCertConfAddReq {
34 kind: TrimString(IamCertKernelKind::MailVCode.to_string()),
35 supplier: None,
36 name: TrimString(IamCertKernelKind::MailVCode.to_string()),
37 note: None,
38 ak_note: add_req.ak_note.clone(),
39 ak_rule: add_req.ak_rule.clone(),
40 sk_note: None,
41 sk_rule: None,
42 ext: None,
43 sk_need: Some(false),
44 sk_dynamic: Some(true),
45 sk_encrypted: Some(false),
46 repeatable: None,
47 is_basic: Some(false),
48 rest_by_kinds: None,
49 expire_sec: None,
50 sk_lock_cycle_sec: None,
51 sk_lock_err_times: None,
52 sk_lock_duration_sec: None,
53 coexist_num: Some(1),
54 conn_uri: None,
55 status: RbumCertConfStatusKind::Enabled,
56 rel_rbum_domain_id: funs.iam_basic_domain_iam_id(),
57 rel_rbum_item_id: rel_iam_item_id,
58 },
59 funs,
60 ctx,
61 )
62 .await?;
63 Ok(id)
64 }
65
66 pub async fn modify_cert_conf(id: &str, modify_req: &IamCertConfMailVCodeAddOrModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
67 RbumCertConfServ::modify_rbum(
68 id,
69 &mut RbumCertConfModifyReq {
70 name: None,
71 note: None,
72 ak_note: modify_req.ak_note.clone(),
73 ak_rule: modify_req.ak_rule.clone(),
74 sk_note: None,
75 sk_rule: None,
76 ext: None,
77 sk_need: None,
78 sk_encrypted: None,
79 repeatable: None,
80 is_basic: None,
81 rest_by_kinds: None,
82 expire_sec: None,
83 sk_lock_cycle_sec: None,
84 sk_lock_err_times: None,
85 sk_lock_duration_sec: None,
86 coexist_num: None,
87 conn_uri: None,
88 status: None,
89 },
90 funs,
91 ctx,
92 )
93 .await?;
94 Ok(())
95 }
96
97 pub async fn add_cert(add_req: &IamCertMailVCodeAddReq, account_id: &str, rel_rbum_cert_conf_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
98 let vcode = Self::get_vcode();
99 let id = RbumCertServ::add_rbum(
100 &mut RbumCertAddReq {
101 ak: TrimString(add_req.mail.trim().to_string()),
102 sk: None,
103 sk_invisible: None,
104 kind: None,
105 supplier: None,
106 vcode: Some(TrimString(vcode.clone())),
107 ext: None,
108 start_time: None,
109 end_time: None,
110 conn_uri: None,
111 status: RbumCertStatusKind::Pending,
112 rel_rbum_cert_conf_id: Some(rel_rbum_cert_conf_id.to_string()),
113 rel_rbum_kind: RbumCertRelKind::Item,
114 rel_rbum_id: account_id.to_string(),
115 is_outside: false,
116 ignore_check_sk: false,
117 },
118 funs,
119 ctx,
120 )
121 .await?;
122 Self::send_activation_mail(account_id, &add_req.mail, &vcode, funs, ctx).await?;
123 Ok(id)
124 }
125
126 pub async fn add_cert_skip_activate(
127 add_req: &IamCertMailVCodeAddReq,
128 account_id: &str,
129 rel_rbum_cert_conf_id: &str,
130 funs: &TardisFunsInst,
131 ctx: &TardisContext,
132 ) -> TardisResult<String> {
133 let vcode = Self::get_vcode();
134 let id = RbumCertServ::add_rbum(
135 &mut RbumCertAddReq {
136 ak: TrimString(add_req.mail.trim().to_string()),
137 sk: None,
138 sk_invisible: None,
139 kind: None,
140 supplier: None,
141 vcode: Some(TrimString(vcode.clone())),
142 ext: None,
143 start_time: None,
144 end_time: None,
145 conn_uri: None,
146 status: RbumCertStatusKind::Enabled,
147 rel_rbum_cert_conf_id: Some(rel_rbum_cert_conf_id.to_string()),
148 rel_rbum_kind: RbumCertRelKind::Item,
149 rel_rbum_id: account_id.to_string(),
150 is_outside: false,
151 ignore_check_sk: false,
152 },
153 funs,
154 ctx,
155 )
156 .await?;
157 Ok(id)
158 }
159
160 pub async fn modify_cert(id: &str, modify_req: &IamCertMailVCodeModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
161 RbumCertServ::modify_rbum(
162 id,
163 &mut RbumCertModifyReq {
164 ak: Some(TrimString(modify_req.mail.to_string())),
165 sk: None,
166 sk_invisible: None,
167
168 ext: None,
169 start_time: None,
170 end_time: None,
171 conn_uri: None,
172 status: None,
173 ignore_check_sk: false,
174 },
175 funs,
176 ctx,
177 )
178 .await?;
179 Ok(())
180 }
181
182 pub async fn add_or_modify_cert(mail: &str, account_id: &str, rel_rbum_cert_conf_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
183 let resp = IamCertServ::get_kernel_cert(account_id, &IamCertKernelKind::MailVCode, funs, ctx).await;
184 match resp {
185 Ok(cert) => {
186 Self::modify_cert(&cert.id, &IamCertMailVCodeModifyReq { mail: mail.to_string() }, funs, ctx).await?;
187 }
188 Err(_) => {
189 Self::add_cert(&IamCertMailVCodeAddReq { mail: mail.to_string() }, account_id, rel_rbum_cert_conf_id, funs, ctx).await?;
190 }
191 }
192 Ok(())
193 }
194
195 pub async fn resend_activation_mail(account_id: &str, mail: &str, cool_down_id_sec: Option<u32>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
196 let vcode = Self::get_vcode();
197 let rel_rbum_cert_conf_id =
198 IamCertServ::get_cert_conf_id_by_kind(IamCertKernelKind::MailVCode.to_string().as_str(), Some(IamTenantServ::get_id_by_ctx(ctx, funs)?), funs).await?;
199 RbumCertServ::add_vcode_to_cache(mail, &vcode, &rel_rbum_cert_conf_id, cool_down_id_sec, funs, ctx).await?;
200 Self::send_activation_mail(account_id, mail, &vcode, funs, ctx).await
201 }
202
203 async fn send_activation_mail(account_id: &str, mail: &str, vcode: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
204 let account_name = IamAccountServ::peek_item(account_id, &IamAccountFilterReq::default(), funs, ctx).await?.name;
205 MailClient::send_cert_activate_vcode(mail, Some(account_name), vcode, funs).await?;
206 Ok(())
207 }
208
209 pub async fn activate_mail(mail: &str, input_vcode: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
210 let ctx = IamAccountServ::new_context_if_account_is_global(ctx, funs).await?;
211 if let Some(cached_vcode) = RbumCertServ::get_vcode_in_cache(mail, &ctx.own_paths, funs).await? {
212 if cached_vcode == input_vcode {
213 let cert = RbumCertServ::find_one_rbum(
214 &RbumCertFilterReq {
215 ak: Some(mail.to_string()),
216 status: Some(RbumCertStatusKind::Pending),
217 rel_rbum_kind: Some(RbumCertRelKind::Item),
218 rel_rbum_cert_conf_ids: Some(vec![
219 IamCertServ::get_cert_conf_id_by_kind(IamCertKernelKind::MailVCode.to_string().as_str(), Some(IamTenantServ::get_id_by_ctx(&ctx, funs)?), funs).await?,
220 ]),
221 ..Default::default()
222 },
223 funs,
224 &ctx,
225 )
226 .await?;
227 return if let Some(cert) = cert {
228 RbumCertServ::modify_rbum(
229 &cert.id,
230 &mut RbumCertModifyReq {
231 status: Some(RbumCertStatusKind::Enabled),
232 ak: None,
233 sk: None,
234 sk_invisible: None,
235
236 ignore_check_sk: false,
237 ext: None,
238 start_time: None,
239 end_time: None,
240 conn_uri: None,
241 },
242 funs,
243 &ctx,
244 )
245 .await?;
246 Ok(())
247 } else {
248 Err(funs.err().not_found(
249 "iam_cert_mail_vcode",
250 "activate",
251 &format!("not found credential of kind {:?}", IamCertKernelKind::MailVCode),
252 "404-iam-cert-kind-not-exist",
253 ))
254 };
255 }
256 }
257 Err(funs.err().unauthorized("iam_cert_mail_vcode", "activate", "email or verification code error", "401-iam-cert-valid"))
258 }
259
260 pub async fn send_bind_mail(mail: &str, cool_down_id_sec: Option<u32>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
261 let ctx = IamAccountServ::new_context_if_account_is_global(ctx, funs).await?;
262 let rel_rbum_cert_conf_id =
263 IamCertServ::get_cert_conf_id_by_kind(IamCertKernelKind::MailVCode.to_string().as_str(), Some(IamTenantServ::get_id_by_ctx(&ctx, funs)?), funs).await?;
264 let vcode = Self::get_vcode();
266 let account_name = IamAccountServ::peek_item(&ctx.owner, &IamAccountFilterReq::default(), funs, &ctx).await?.name;
267 RbumCertServ::add_vcode_to_cache(mail, &vcode, &rel_rbum_cert_conf_id, cool_down_id_sec, funs, &ctx).await?;
268 MailClient::send_cert_activate_vcode(mail, Some(account_name), &vcode, funs).await?;
269 Ok(())
270 }
271
272 pub async fn bind_mail(mail: &str, input_vcode: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
273 let ctx = IamAccountServ::new_context_if_account_is_global(ctx, funs).await?;
274 if let Some(cached_vcode) = RbumCertServ::get_vcode_in_cache(mail, &ctx.own_paths, funs).await? {
275 if cached_vcode == input_vcode {
276 let rel_rbum_cert_conf_id =
277 IamCertServ::get_cert_conf_id_by_kind(IamCertKernelKind::MailVCode.to_string().as_str(), Some(IamTenantServ::get_id_by_ctx(&ctx, funs)?), funs).await?;
278 Self::check_mail_bound(mail, vec![rel_rbum_cert_conf_id.clone()], funs, &ctx).await?;
279 let id = if Self::check_account_bind_mail(vec![rel_rbum_cert_conf_id.clone()], &ctx.owner.clone(), funs, &ctx).await.is_ok() {
280 RbumCertServ::add_rbum(
281 &mut RbumCertAddReq {
282 ak: TrimString(mail.trim().to_string()),
283 sk: None,
284 sk_invisible: None,
285 kind: None,
286 supplier: None,
287 vcode: Some(TrimString(input_vcode.to_string())),
288 ext: None,
289 start_time: None,
290 end_time: None,
291 conn_uri: None,
292 status: RbumCertStatusKind::Enabled,
293 rel_rbum_cert_conf_id: Some(rel_rbum_cert_conf_id),
294 rel_rbum_kind: RbumCertRelKind::Item,
295 rel_rbum_id: ctx.owner.clone(),
296 is_outside: false,
297 ignore_check_sk: false,
298 },
299 funs,
300 &ctx,
301 )
302 .await?
303 } else {
304 let id = RbumCertServ::find_id_rbums(
305 &RbumCertFilterReq {
306 status: Some(RbumCertStatusKind::Enabled),
307 rel_rbum_id: Some(ctx.owner.clone()),
308 rel_rbum_kind: Some(RbumCertRelKind::Item),
309 rel_rbum_cert_conf_ids: Some(vec![rel_rbum_cert_conf_id]),
310 ..Default::default()
311 },
312 None,
313 None,
314 funs,
315 &ctx,
316 )
317 .await?
318 .pop()
319 .ok_or_else(|| funs.err().unauthorized("iam_cert_mail_vcode", "activate", "email or verification code error", "401-iam-cert-valid"))?;
320 RbumCertServ::modify_rbum(
321 &id,
322 &mut RbumCertModifyReq {
323 ak: Some(TrimString(mail.trim().to_string())),
324 sk: None,
325 sk_invisible: None,
326 ignore_check_sk: true,
327 ext: None,
328 start_time: None,
329 end_time: None,
330 conn_uri: None,
331 status: None,
332 },
333 funs,
334 &ctx,
335 )
336 .await?;
337 id
338 };
339 IamSearchClient::async_add_or_modify_account_search(&ctx.owner, Box::new(true), "", funs, &ctx).await?;
340 let op_describe = format!("绑定邮箱为{}", mail);
341 let _ = IamLogClient::add_ctx_task(LogParamTag::IamAccount, Some(ctx.owner.to_string()), op_describe, Some("BindMailbox".to_string()), &ctx).await;
342
343 return Ok(id);
344 }
345 }
346 Err(funs.err().unauthorized("iam_cert_mail_vcode", "activate", "email or verification code error", "401-iam-cert-valid"))
347 }
348
349 async fn check_account_bind_mail(rel_rbum_cert_conf_ids: Vec<String>, rel_rbum_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
350 if RbumCertServ::count_rbums(
352 &RbumCertFilterReq {
353 basic: RbumBasicFilterReq {
354 own_paths: Some("".to_string()),
355 with_sub_own_paths: true,
356 ..Default::default()
357 },
358 rel_rbum_id: Some(rel_rbum_id.to_owned()),
359 rel_rbum_kind: Some(RbumCertRelKind::Item),
360 rel_rbum_cert_conf_ids: Some(rel_rbum_cert_conf_ids.clone()),
361 ..Default::default()
362 },
363 funs,
364 ctx,
365 )
366 .await?
367 > 0
368 {
369 return Err(funs.err().conflict("iam_cert_mail_vcode", "bind", "email already exist bind", "409-iam-cert-email-bind-already-exist"));
370 }
371 Ok(())
372 }
373
374 async fn check_mail_bound(mail: &str, rel_rbum_cert_conf_ids: Vec<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
375 if RbumCertServ::count_rbums(
377 &RbumCertFilterReq {
378 basic: RbumBasicFilterReq {
379 own_paths: Some("".to_string()),
380 with_sub_own_paths: true,
381 ..Default::default()
382 },
383 ak: Some(mail.to_string()),
384 rel_rbum_kind: Some(RbumCertRelKind::Item),
385 rel_rbum_cert_conf_ids: Some(rel_rbum_cert_conf_ids),
386 ..Default::default()
387 },
388 funs,
389 ctx,
390 )
391 .await?
392 > 0
393 {
394 return Err(funs.err().unauthorized("iam_cert_mail_vcode", "activate", "email already exist", "404-iam-cert-email-not-exist"));
395 }
396 Ok(())
397 }
398
399 pub async fn send_login_mail(mail: &str, tenant_id: &str, cool_down_id_sec: Option<u32>, funs: &TardisFunsInst) -> TardisResult<()> {
400 let own_paths = tenant_id.to_string();
401 let mock_ctx = TardisContext {
402 own_paths: own_paths.to_string(),
403 ..Default::default()
404 };
405 let global_rbum_cert_conf_id = IamCertServ::get_cert_conf_id_by_kind(&IamCertKernelKind::MailVCode.to_string(), None, funs).await?;
406 let tenant_rbum_cert_conf_id = IamCertServ::get_cert_conf_id_by_kind(&IamCertKernelKind::MailVCode.to_string(), Some(tenant_id.to_owned()), funs).await?;
407 if RbumCertServ::count_rbums(
408 &RbumCertFilterReq {
409 basic: RbumBasicFilterReq {
410 own_paths: Some("".to_string()),
411 with_sub_own_paths: true,
412 ..Default::default()
413 },
414 ak: Some(mail.to_string()),
415 rel_rbum_kind: Some(RbumCertRelKind::Item),
416 rel_rbum_cert_conf_ids: Some(vec![tenant_rbum_cert_conf_id.clone()]),
417 ..Default::default()
418 },
419 funs,
420 &mock_ctx,
421 )
422 .await?
423 > 0
424 {
425 let vcode = Self::get_vcode();
426 RbumCertServ::add_vcode_to_cache(mail, &vcode, &tenant_rbum_cert_conf_id, cool_down_id_sec, funs, &mock_ctx).await?;
427 MailClient::send_vcode(mail, None, &vcode, funs).await?;
428 return Ok(());
429 }
430 if RbumCertServ::count_rbums(
431 &RbumCertFilterReq {
432 basic: RbumBasicFilterReq {
433 own_paths: Some("".to_string()),
434 with_sub_own_paths: true,
435 ..Default::default()
436 },
437 ak: Some(mail.to_string()),
438 rel_rbum_kind: Some(RbumCertRelKind::Item),
439 rel_rbum_cert_conf_ids: Some(vec![global_rbum_cert_conf_id.clone()]),
440 ..Default::default()
441 },
442 funs,
443 &mock_ctx,
444 )
445 .await?
446 > 0
447 {
448 let vcode = Self::get_vcode();
449 RbumCertServ::add_vcode_to_cache(
450 mail,
451 &vcode,
452 &global_rbum_cert_conf_id,
453 cool_down_id_sec,
454 funs,
455 &TardisContext {
456 own_paths: "".to_string(),
457 ..Default::default()
458 },
459 )
460 .await?;
461 MailClient::send_vcode(mail, None, &vcode, funs).await?;
462 return Ok(());
463 }
464 return Err(funs.err().not_found("iam_cert_phone_vcode", "send", "email not find", "404-iam-cert-email-not-exist"));
465 }
466
467 fn get_vcode() -> String {
468 let mut rand = tardis::rand::thread_rng();
469 let vcode: i32 = rand.gen_range(1000..9999);
470 format!("{vcode}")
471 }
472
473 pub async fn add_or_enable_cert_conf(
474 add_req: &IamCertConfMailVCodeAddOrModifyReq,
475 rel_iam_item_id: Option<String>,
476 funs: &TardisFunsInst,
477 ctx: &TardisContext,
478 ) -> TardisResult<String> {
479 let cert_result = RbumCertConfServ::do_find_one_rbum(
480 &RbumCertConfFilterReq {
481 kind: Some(TrimString(IamCertKernelKind::MailVCode.to_string())),
482 rel_rbum_item_id: rel_iam_item_id.clone(),
483 ..Default::default()
484 },
485 funs,
486 ctx,
487 )
488 .await?;
489 let result = if let Some(cert_result) = cert_result {
490 IamCertServ::enabled_cert_conf(&cert_result.id, funs, ctx).await?;
491 cert_result.id
492 } else {
493 Self::add_cert_conf(add_req, rel_iam_item_id, funs, ctx).await?
494 };
495 Ok(result)
496 }
497
498 pub async fn send_pwd(account_id: &str, pwd: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
499 let resp = IamCertServ::get_kernel_cert(account_id, &IamCertKernelKind::MailVCode, funs, ctx).await;
500 match resp {
501 Ok(cert) => {
502 let _ = MailClient::async_send_pwd(&cert.ak, pwd, funs, ctx).await;
503 }
504 Err(_) => info!("mail pwd not found"),
505 }
506 Ok(())
507 }
508}