bios_iam/basic/serv/
iam_cert_oauth2_serv.rs

1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
5use tardis::basic::dto::TardisContext;
6use tardis::basic::field::TrimString;
7use tardis::basic::result::TardisResult;
8use tardis::{TardisFuns, TardisFunsInst};
9
10use crate::basic::dto::iam_account_dto::IamAccountAggAddReq;
11use crate::basic::dto::iam_cert_conf_dto::{IamCertConfOAuth2AddOrModifyReq, IamCertConfOAuth2Resp};
12use crate::basic::dto::iam_cert_dto::IamCertOAuth2AddOrModifyReq;
13use crate::basic::dto::iam_filer_dto::IamTenantFilterReq;
14use crate::basic::serv::iam_cert_user_pwd_serv::IamCertUserPwdServ;
15use crate::basic::serv::oauth2_spi::iam_cert_oauth2_spi_github::IamCertOAuth2SpiGithub;
16use crate::iam_config::IamBasicConfigApi;
17use crate::iam_enumeration::{IamCertExtKind, IamCertOAuth2Supplier};
18use bios_basic::rbum::dto::rbum_cert_conf_dto::{RbumCertConfAddReq, RbumCertConfModifyReq};
19use bios_basic::rbum::dto::rbum_cert_dto::{RbumCertAddReq, RbumCertModifyReq};
20use bios_basic::rbum::dto::rbum_filer_dto::{RbumCertConfFilterReq, RbumCertFilterReq};
21use bios_basic::rbum::rbum_enumeration::RbumCertStatusKind::Pending;
22use bios_basic::rbum::rbum_enumeration::{RbumCertConfStatusKind, RbumCertRelKind, RbumCertStatusKind};
23use bios_basic::rbum::serv::rbum_cert_serv::{RbumCertConfServ, RbumCertServ};
24use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
25
26use super::clients::iam_search_client::IamSearchClient;
27use super::iam_account_serv::IamAccountServ;
28use super::iam_cert_serv::IamCertServ;
29use super::iam_tenant_serv::IamTenantServ;
30use super::oauth2_spi::iam_cert_oauth2_spi_wechat_mp::IamCertOAuth2SpiWeChatMp;
31
32pub struct IamCertOAuth2Serv;
33
34impl IamCertOAuth2Serv {
35    pub async fn add_cert_conf(
36        cert_supplier: IamCertOAuth2Supplier,
37        add_req: &IamCertConfOAuth2AddOrModifyReq,
38        rel_iam_item_id: &str,
39        funs: &TardisFunsInst,
40        ctx: &TardisContext,
41    ) -> TardisResult<String> {
42        RbumCertConfServ::add_rbum(
43            &mut RbumCertConfAddReq {
44                kind: TrimString(IamCertExtKind::OAuth2.to_string()),
45                supplier: Some(TrimString(cert_supplier.to_string())),
46                name: TrimString(format!("{}{}", IamCertExtKind::OAuth2, cert_supplier)),
47                note: None,
48                ak_note: None,
49                ak_rule: None,
50                sk_note: None,
51                sk_rule: None,
52                ext: Some(TardisFuns::json.obj_to_string(&add_req)?),
53                sk_need: Some(false),
54                sk_dynamic: Some(false),
55                sk_encrypted: Some(false),
56                repeatable: None,
57                is_basic: Some(false),
58                rest_by_kinds: None,
59                expire_sec: None,
60                sk_lock_cycle_sec: None,
61                sk_lock_err_times: None,
62                sk_lock_duration_sec: None,
63                coexist_num: Some(1),
64                conn_uri: None,
65                status: RbumCertConfStatusKind::Enabled,
66                rel_rbum_domain_id: funs.iam_basic_domain_iam_id(),
67                rel_rbum_item_id: Some(rel_iam_item_id.to_string()),
68            },
69            funs,
70            ctx,
71        )
72        .await
73    }
74
75    pub async fn modify_cert_conf(id: &str, modify_req: &IamCertConfOAuth2AddOrModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
76        RbumCertConfServ::modify_rbum(
77            id,
78            &mut RbumCertConfModifyReq {
79                name: None,
80                note: None,
81                ak_note: None,
82                ak_rule: None,
83                sk_note: None,
84                sk_rule: None,
85                ext: Some(TardisFuns::json.obj_to_string(&modify_req)?),
86                sk_need: None,
87                sk_encrypted: None,
88                repeatable: None,
89                is_basic: None,
90                rest_by_kinds: None,
91                expire_sec: None,
92                sk_lock_cycle_sec: None,
93                sk_lock_err_times: None,
94                sk_lock_duration_sec: None,
95                coexist_num: None,
96                conn_uri: None,
97                status: None,
98            },
99            funs,
100            ctx,
101        )
102        .await
103    }
104
105    pub async fn get_cert_conf(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamCertConfOAuth2Resp> {
106        RbumCertConfServ::get_rbum(id, &RbumCertConfFilterReq::default(), funs, ctx)
107            .await
108            .map(|i: bios_basic::rbum::dto::rbum_cert_conf_dto::RbumCertConfDetailResp| TardisFuns::json.str_to_obj(&i.ext))?
109    }
110
111    pub async fn add_or_modify_cert(
112        add_or_modify_req: &IamCertOAuth2AddOrModifyReq,
113        account_id: &str,
114        rel_rbum_cert_conf_id: &str,
115        funs: &TardisFunsInst,
116        ctx: &TardisContext,
117    ) -> TardisResult<()> {
118        let cert_id = RbumCertServ::find_id_rbums(
119            &RbumCertFilterReq {
120                rel_rbum_cert_conf_ids: Some(vec![rel_rbum_cert_conf_id.to_string()]),
121                rel_rbum_id: Some(account_id.to_string()),
122                ..Default::default()
123            },
124            None,
125            None,
126            funs,
127            ctx,
128        )
129        .await?;
130        if let Some(cert_id) = cert_id.first() {
131            RbumCertServ::modify_rbum(
132                cert_id,
133                &mut RbumCertModifyReq {
134                    ak: Some(add_or_modify_req.open_id.clone()),
135                    sk: None,
136                    sk_invisible: None,
137
138                    ignore_check_sk: false,
139                    ext: None,
140                    start_time: None,
141                    end_time: None,
142                    conn_uri: None,
143                    status: None,
144                },
145                funs,
146                ctx,
147            )
148            .await?;
149        } else {
150            RbumCertServ::add_rbum(
151                &mut RbumCertAddReq {
152                    ak: add_or_modify_req.open_id.clone(),
153                    sk: None,
154                    sk_invisible: None,
155
156                    kind: None,
157                    supplier: None,
158                    vcode: None,
159                    ext: None,
160                    start_time: None,
161                    end_time: None,
162                    conn_uri: None,
163                    status: RbumCertStatusKind::Enabled,
164                    rel_rbum_cert_conf_id: Some(rel_rbum_cert_conf_id.to_string()),
165                    rel_rbum_kind: RbumCertRelKind::Item,
166                    rel_rbum_id: account_id.to_string(),
167                    is_outside: false,
168                    ignore_check_sk: false,
169                },
170                funs,
171                ctx,
172            )
173            .await?;
174        };
175        Ok(())
176    }
177
178    pub async fn get_cert_rel_account_by_open_id(open_id: &str, rel_rbum_cert_conf_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<String>> {
179        let result = RbumCertServ::find_rbums(
180            &RbumCertFilterReq {
181                rel_rbum_cert_conf_ids: Some(vec![rel_rbum_cert_conf_id.to_string()]),
182                ak: Some(open_id.to_string()),
183                ..Default::default()
184            },
185            None,
186            None,
187            funs,
188            ctx,
189        )
190        .await?
191        .first()
192        .map(|r| r.rel_rbum_id.to_string());
193        Ok(result)
194    }
195
196    pub async fn get_or_add_account(cert_supplier: IamCertOAuth2Supplier, code: &str, tenant_id: &str, funs: &TardisFunsInst) -> TardisResult<(String, String)> {
197        let cert_conf_id =
198            IamCertServ::get_cert_conf_id_by_kind_supplier(&IamCertExtKind::OAuth2.to_string(), &cert_supplier.to_string(), Some(tenant_id.to_string()), funs).await?;
199        let mut mock_ctx = TardisContext {
200            own_paths: tenant_id.to_string(),
201            ..Default::default()
202        };
203        let cert_conf = Self::get_cert_conf(&cert_conf_id, funs, &mock_ctx).await?;
204        let client = Self::get_access_token_func(cert_supplier);
205        let oauth_token_info = client.get_access_token(code, &cert_conf.ak, &cert_conf.sk, funs).await?;
206        if let Some(account_id) = Self::get_cert_rel_account_by_open_id(&oauth_token_info.open_id, &cert_conf_id, funs, &mock_ctx).await? {
207            return Ok((account_id, oauth_token_info.access_token));
208        }
209        if !IamTenantServ::get_item(tenant_id, &IamTenantFilterReq::default(), funs, &mock_ctx).await?.account_self_reg {
210            return Err(funs.err().not_found(
211                "rbum_cert",
212                "get_or_add_account",
213                &format!("not found oauth2 cert(openid): {} and self-registration disabled", &oauth_token_info.open_id),
214                "401-rbum-cert-valid-error",
215            ));
216        }
217        // Register
218        mock_ctx.owner = TardisFuns::field.nanoid();
219        let account_id = IamAccountServ::add_account_agg(
220            &IamAccountAggAddReq {
221                id: Some(TrimString(mock_ctx.owner.clone())),
222                name: TrimString(client.get_account_name(oauth_token_info.clone(), funs).await?),
223                cert_user_name: IamCertUserPwdServ::rename_ak_if_duplicate(&TardisFuns::field.nanoid_len(8).to_lowercase(), funs, &mock_ctx).await?,
224                // FIXME 临时密码
225                cert_password: Some(TrimString(format!("{}0Pw$", TardisFuns::field.nanoid_len(6)))),
226                cert_phone: None,
227                cert_mail: None,
228                role_ids: None,
229                org_node_ids: None,
230                scope_level: None,
231                disabled: None,
232                icon: None,
233                exts: HashMap::new(),
234                status: Some(Pending),
235                temporary: None,
236                lock_status: None,
237                logout_type: None,
238                labor_type: None,
239            },
240            false,
241            funs,
242            &mock_ctx,
243        )
244        .await?;
245        Self::add_or_modify_cert(
246            &IamCertOAuth2AddOrModifyReq {
247                open_id: TrimString(oauth_token_info.open_id.to_string()),
248            },
249            &account_id,
250            &cert_conf_id,
251            funs,
252            &mock_ctx,
253        )
254        .await?;
255        IamSearchClient::async_add_or_modify_account_search(&account_id, Box::new(false), "", funs, &mock_ctx).await?;
256        mock_ctx.execute_task().await?;
257        Ok((account_id, oauth_token_info.access_token))
258    }
259
260    fn get_access_token_func(supplier: IamCertOAuth2Supplier) -> Box<dyn IamCertOAuth2Spi> {
261        match supplier {
262            // IamCertOAuth2Supplier::Weibo => {}
263            IamCertOAuth2Supplier::Github => Box::new(IamCertOAuth2SpiGithub),
264            IamCertOAuth2Supplier::WechatMp => Box::new(IamCertOAuth2SpiWeChatMp),
265        }
266    }
267
268    pub async fn add_or_enable_cert_conf(
269        supplier: IamCertOAuth2Supplier,
270        add_req: &IamCertConfOAuth2AddOrModifyReq,
271        rel_iam_item_id: &str,
272        funs: &TardisFunsInst,
273        ctx: &TardisContext,
274    ) -> TardisResult<String> {
275        let cert_result = RbumCertConfServ::do_find_one_rbum(
276            &RbumCertConfFilterReq {
277                kind: Some(TrimString(IamCertExtKind::OAuth2.to_string())),
278                supplier: Some(supplier.clone().to_string()),
279                rel_rbum_item_id: Some(rel_iam_item_id.into()),
280                ..Default::default()
281            },
282            funs,
283            ctx,
284        )
285        .await?;
286        let result = if let Some(cert_result) = cert_result {
287            IamCertServ::enabled_cert_conf(&cert_result.id, funs, ctx).await?;
288            cert_result.id
289        } else {
290            Self::add_cert_conf(supplier, add_req, rel_iam_item_id, funs, ctx).await?
291        };
292        Ok(result)
293    }
294}
295
296#[async_trait]
297pub trait IamCertOAuth2Spi: Send + Sync {
298    async fn get_access_token(&self, code: &str, ak: &str, sk: &str, funs: &TardisFunsInst) -> TardisResult<IamCertOAuth2TokenInfo>;
299    async fn get_account_name(&self, oauth2_info: IamCertOAuth2TokenInfo, funs: &TardisFunsInst) -> TardisResult<String>;
300}
301
302#[derive(Clone)]
303pub struct IamCertOAuth2TokenInfo {
304    pub open_id: String,
305    pub access_token: String,
306    pub refresh_token: Option<String>,
307    pub token_expires_ms: Option<u32>,
308    pub union_id: Option<String>,
309}