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 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 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::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}