bios_iam/basic/serv/
iam_cert_mail_vcode_serv.rs

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        // Self::check_bind_mail(mail, vec![rel_rbum_cert_conf_id], &ctx.owner, funs, &ctx).await?;
265        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        // check bind or not
351        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        // check existence or not
376        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}