1use std::collections::{HashMap, HashSet};
2
3use async_trait::async_trait;
4use bios_basic::rbum::rbum_config::RbumConfigApi;
5use bios_basic::rbum::rbum_enumeration::RbumSetCateLevelQueryKind;
6use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
7use bios_basic::rbum::serv::rbum_set_serv::{RbumSetCateServ, RbumSetItemServ};
8use itertools::Itertools;
9use ldap3::log::warn;
10use tardis::basic::dto::TardisContext;
11use tardis::basic::field::TrimString;
12use tardis::basic::result::TardisResult;
13use tardis::db::sea_orm::sea_query::{Expr, SelectStatement};
14use tardis::db::sea_orm::*;
15use tardis::futures::future::BoxFuture;
16use tardis::futures::FutureExt;
17use tardis::futures_util::future::join_all;
18use tardis::web::web_resp::TardisPage;
19use tardis::TardisFunsInst;
20
21use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumSetItemFilterReq};
22use bios_basic::rbum::dto::rbum_item_dto::{RbumItemKernelAddReq, RbumItemKernelModifyReq};
23use bios_basic::rbum::dto::rbum_rel_dto::RbumRelBoneResp;
24use bios_basic::rbum::dto::rbum_set_cate_dto::RbumSetCateAddReq;
25use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
26
27use crate::basic::domain::iam_res;
28use crate::basic::dto::iam_filer_dto::IamResFilterReq;
29use crate::basic::dto::iam_res_dto::{IamResAddReq, IamResAggAddReq, IamResDetailResp, IamResModifyReq, IamResSummaryResp, JsonMenu, MenuItem};
30use crate::basic::dto::iam_set_dto::{IamSetItemAddReq, IamSetItemAggAddReq};
31use crate::basic::serv::iam_key_cache_serv::IamResCacheServ;
32use crate::basic::serv::iam_rel_serv::IamRelServ;
33use crate::basic::serv::iam_set_serv::IamSetServ;
34use crate::iam_config::IamBasicInfoManager;
35use crate::iam_constants;
36use crate::iam_enumeration::{IamRelKind, IamResKind, IamSetCateKind};
37
38use super::clients::iam_log_client::{IamLogClient, LogParamTag};
39use super::iam_account_serv::IamAccountServ;
40use super::iam_cert_serv::IamCertServ;
41use super::iam_key_cache_serv::IamCacheResRelAddOrModifyReq;
42use super::iam_role_serv::IamRoleServ;
43
44pub struct IamResServ;
45
46pub struct IamMenuServ;
47
48#[async_trait]
49impl RbumItemCrudOperation<iam_res::ActiveModel, IamResAddReq, IamResModifyReq, IamResSummaryResp, IamResDetailResp, IamResFilterReq> for IamResServ {
50 fn get_ext_table_name() -> &'static str {
51 iam_res::Entity.table_name()
52 }
53
54 fn get_rbum_kind_id() -> Option<String> {
55 Some(IamBasicInfoManager::get_config(|conf| conf.kind_res_id.clone()))
56 }
57
58 fn get_rbum_domain_id() -> Option<String> {
59 Some(IamBasicInfoManager::get_config(|conf| conf.domain_iam_id.clone()))
60 }
61
62 async fn package_item_add(add_req: &IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<RbumItemKernelAddReq> {
63 Ok(RbumItemKernelAddReq {
64 code: Some(add_req.code.clone()),
65 name: add_req.name.clone(),
66 disabled: add_req.disabled,
67 scope_level: add_req.scope_level.clone(),
68 ..Default::default()
69 })
70 }
71
72 async fn package_ext_add(id: &str, add_req: &IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_res::ActiveModel> {
73 Ok(iam_res::ActiveModel {
74 id: Set(id.to_string()),
75 kind: Set(add_req.kind.to_int()),
76 icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
77 sort: Set(add_req.sort.unwrap_or(0)),
78 method: Set(add_req.method.as_ref().unwrap_or(&TrimString("*".to_string())).to_string()),
79 hide: Set(add_req.hide.unwrap_or(false)),
80 action: Set(add_req.action.as_ref().unwrap_or(&"".to_string()).to_string()),
81 crypto_req: Set(add_req.crypto_req.unwrap_or(false)),
82 crypto_resp: Set(add_req.crypto_resp.unwrap_or(false)),
83 double_auth: Set(add_req.double_auth.unwrap_or(false)),
84 double_auth_msg: Set(add_req.double_auth_msg.as_ref().unwrap_or(&"".to_string()).to_string()),
85 ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
86 need_login: Set(add_req.need_login.unwrap_or(false)),
87 ..Default::default()
88 })
89 }
90
91 async fn before_add_item(add_req: &mut IamResAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
92 add_req.encoding();
93 Ok(())
94 }
95
96 async fn after_add_item(id: &str, _: &mut IamResAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
97 let res = Self::peek_item(
98 id,
99 &IamResFilterReq {
100 basic: RbumBasicFilterReq {
101 with_sub_own_paths: true,
102 ..Default::default()
103 },
104 ..Default::default()
105 },
106 funs,
107 ctx,
108 )
109 .await?;
110 if res.kind == IamResKind::Api {
111 IamResCacheServ::add_res(&res.code, &res.method, res.crypto_req, res.crypto_resp, res.double_auth, res.need_login, funs).await?;
112 }
113 let (op_describe, op_kind) = match res.kind {
114 IamResKind::Menu => ("添加目录页面".to_string(), "AddContentPageaspersonal".to_string()),
115 IamResKind::Api => ("添加API".to_string(), "AddApi".to_string()),
116 IamResKind::Ele => ("添加目录页面按钮".to_string(), "AddContentPageButton".to_string()),
117 IamResKind::Product => ("添加产品".to_string(), "AddProduct".to_string()),
118 IamResKind::Spec => ("添加产品规格".to_string(), "AddSpecification".to_string()),
119 };
120 if !op_describe.is_empty() {
121 let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
122 }
123
124 Ok(())
125 }
126
127 async fn before_modify_item(id: &str, modify_req: &mut IamResModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
128 if modify_req.code.is_some() || modify_req.method.is_some() {
129 let item = Self::get_item(id, &IamResFilterReq::default(), funs, ctx).await?;
130 modify_req.encoding(
131 item.kind.clone(),
132 if let Some(method) = &modify_req.method {
133 method.to_string()
134 } else {
135 item.method.clone()
136 },
137 );
138 }
139 Ok(())
140 }
141
142 async fn package_item_modify(_: &str, modify_req: &IamResModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>> {
143 if modify_req.name.is_none() && modify_req.scope_level.is_none() && modify_req.disabled.is_none() && modify_req.code.is_none() {
144 return Ok(None);
145 }
146 Ok(Some(RbumItemKernelModifyReq {
147 code: modify_req.code.clone(),
148 name: modify_req.name.clone(),
149 scope_level: modify_req.scope_level.clone(),
150 disabled: modify_req.disabled,
151 }))
152 }
153
154 async fn package_ext_modify(id: &str, modify_req: &IamResModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<iam_res::ActiveModel>> {
155 if modify_req.icon.is_none()
156 && modify_req.sort.is_none()
157 && modify_req.hide.is_none()
158 && modify_req.action.is_none()
159 && modify_req.method.is_none()
160 && modify_req.crypto_req.is_none()
161 && modify_req.crypto_resp.is_none()
162 && modify_req.double_auth.is_none()
163 {
164 return Ok(None);
165 }
166 let mut iam_res = iam_res::ActiveModel {
167 id: Set(id.to_string()),
168 ..Default::default()
169 };
170 if let Some(icon) = &modify_req.icon {
171 iam_res.icon = Set(icon.to_string());
172 }
173 if let Some(sort) = modify_req.sort {
174 iam_res.sort = Set(sort);
175 }
176 if let Some(hide) = modify_req.hide {
177 iam_res.hide = Set(hide);
178 }
179 if let Some(action) = &modify_req.action {
180 iam_res.action = Set(action.to_string());
181 }
182 if let Some(method) = &modify_req.method {
183 iam_res.method = Set(method.to_string());
184 }
185 if let Some(crypto_req) = modify_req.crypto_req {
186 iam_res.crypto_req = Set(crypto_req);
187 }
188 if let Some(crypto_resp) = modify_req.crypto_resp {
189 iam_res.crypto_resp = Set(crypto_resp);
190 }
191 if let Some(double_auth) = modify_req.double_auth {
192 iam_res.double_auth = Set(double_auth);
193 }
194 if let Some(double_auth_msg) = &modify_req.double_auth_msg {
195 iam_res.double_auth_msg = Set(double_auth_msg.to_string());
196 }
197 if let Some(need_login) = modify_req.need_login {
198 iam_res.need_login = Set(need_login);
199 }
200 if let Some(ext) = &modify_req.ext {
201 iam_res.ext = Set(ext.to_string());
202 }
203 Ok(Some(iam_res))
204 }
205
206 async fn after_modify_item(id: &str, modify_req: &mut IamResModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
207 let res = Self::peek_item(
208 id,
209 &IamResFilterReq {
210 basic: RbumBasicFilterReq {
211 with_sub_own_paths: true,
212 ..Default::default()
213 },
214 ..Default::default()
215 },
216 funs,
217 ctx,
218 )
219 .await?;
220 if modify_req.crypto_req.is_some() || modify_req.crypto_resp.is_some() || modify_req.double_auth.is_some() || modify_req.method.is_some() {
221 IamResCacheServ::add_or_modify_res_rel(
222 &res.code,
223 &res.method,
224 &IamCacheResRelAddOrModifyReq {
225 st: None,
226 et: None,
227 accounts: vec![],
228 roles: vec![],
229 groups: vec![],
230 apps: vec![],
231 tenants: vec![],
232 aks: vec![],
233 need_crypto_req: modify_req.crypto_req,
234 need_crypto_resp: modify_req.crypto_resp,
235 need_double_auth: modify_req.double_auth,
236 need_login: modify_req.need_login,
237 },
238 funs,
239 )
240 .await?;
241 }
242 if let Some(disabled) = modify_req.disabled {
243 if res.kind == IamResKind::Api {
244 if disabled {
245 IamResCacheServ::delete_res(&res.code, &res.method, funs).await?;
246 } else {
247 IamResCacheServ::add_res(&res.code, &res.method, res.crypto_req, res.crypto_resp, res.double_auth, res.need_login, funs).await?;
248 }
249 }
250 }
251 if let Some(bind_api_res) = &modify_req.bind_api_res {
252 let old_api_res = IamResServ::find_to_simple_rel_roles(&IamRelKind::IamResApi, id, None, None, funs, ctx).await?.into_iter().map(|rel| rel.rel_id).collect_vec();
253 if old_api_res != *bind_api_res {
254 for del_to_item_id in old_api_res {
255 IamRelServ::delete_simple_rel(&IamRelKind::IamResApi, &del_to_item_id, id, funs, ctx).await?;
256 }
257 for add_to_item_id in bind_api_res {
258 IamRelServ::add_simple_rel(&IamRelKind::IamResApi, add_to_item_id, id, None, None, false, false, funs, ctx).await?;
259 }
260 }
261 }
262 let (op_describe, op_kind) = match res.kind {
263 IamResKind::Menu => ("编辑目录页面".to_string(), "ModifyContentPage".to_string()),
264 IamResKind::Api => ("编辑API".to_string(), "ModifyApi".to_string()),
265 IamResKind::Ele => ("编辑操作".to_string(), "ModifyEle".to_string()),
266 IamResKind::Product => ("编辑产品".to_string(), "ModifyProduct".to_string()),
267 IamResKind::Spec => ("编辑产品规格".to_string(), "ModifySpecification".to_string()),
268 };
269 if !op_describe.is_empty() {
270 let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
271 }
272
273 Ok(())
274 }
275
276 async fn before_delete_item(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResDetailResp>> {
277 Ok(Some(
278 Self::get_item(
279 id,
280 &IamResFilterReq {
281 basic: RbumBasicFilterReq {
282 with_sub_own_paths: true,
283 ..Default::default()
284 },
285 ..Default::default()
286 },
287 funs,
288 ctx,
289 )
290 .await?,
291 ))
292 }
293
294 async fn after_delete_item(_: &str, deleted_item: &Option<IamResDetailResp>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
295 if let Some(deleted_item) = deleted_item {
296 if deleted_item.kind == IamResKind::Api {
297 IamResCacheServ::delete_res(&deleted_item.code, &deleted_item.method, funs).await?;
298 }
299 let (op_describe, op_kind) = match deleted_item.kind {
300 IamResKind::Menu => ("删除目录页面".to_string(), "DeleteContentPageAsPersonal".to_string()),
301 IamResKind::Api => ("删除API".to_string(), "DeleteApi".to_string()),
302 IamResKind::Ele => ("移除目录页面按钮".to_string(), "RemoveContentPageButton".to_string()),
303 IamResKind::Product => ("移除产品".to_string(), "RemoveProduct".to_string()),
304 IamResKind::Spec => ("移除产品规格".to_string(), "RemoveSpecification".to_string()),
305 };
306 if !op_describe.is_empty() {
307 let _ = IamLogClient::add_ctx_task(LogParamTag::IamRes, Some(deleted_item.id.to_string()), op_describe, Some(op_kind), ctx).await;
308 }
309
310 Ok(())
311 } else {
312 Err(funs.err().not_found(&Self::get_obj_name(), "delete", "not found resource", "404-iam-res-not-exist"))
313 }
314 }
315
316 async fn package_ext_query(query: &mut SelectStatement, _: bool, filter: &IamResFilterReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
317 query.column((iam_res::Entity, iam_res::Column::Kind));
318 query.column((iam_res::Entity, iam_res::Column::Icon));
319 query.column((iam_res::Entity, iam_res::Column::Sort));
320 query.column((iam_res::Entity, iam_res::Column::Method));
321 query.column((iam_res::Entity, iam_res::Column::Hide));
322 query.column((iam_res::Entity, iam_res::Column::Action));
323 query.column((iam_res::Entity, iam_res::Column::CryptoReq));
324 query.column((iam_res::Entity, iam_res::Column::CryptoResp));
325 query.column((iam_res::Entity, iam_res::Column::DoubleAuth));
326 query.column((iam_res::Entity, iam_res::Column::DoubleAuthMsg));
327 query.column((iam_res::Entity, iam_res::Column::NeedLogin));
328 query.column((iam_res::Entity, iam_res::Column::Ext));
329 if let Some(kind) = &filter.kind {
330 query.and_where(Expr::col(iam_res::Column::Kind).eq(kind.to_int()));
331 }
332 Ok(())
333 }
334
335 async fn peek_item(id: &str, filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamResSummaryResp> {
336 let res = Self::do_peek_item(id, filter, funs, ctx).await?;
337 Ok(res.decoding())
338 }
339
340 async fn get_item(id: &str, filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamResDetailResp> {
341 let res = Self::do_get_item(id, filter, funs, ctx).await?;
342 Ok(res.decoding())
343 }
344
345 async fn paginate_items(
346 filter: &IamResFilterReq,
347 page_number: u32,
348 page_size: u32,
349 desc_sort_by_create: Option<bool>,
350 desc_sort_by_update: Option<bool>,
351 funs: &TardisFunsInst,
352 ctx: &TardisContext,
353 ) -> TardisResult<TardisPage<IamResSummaryResp>> {
354 let mut res = Self::do_paginate_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
355 res.records = res.records.into_iter().map(|r| r.decoding()).collect();
356 Ok(res)
357 }
358
359 async fn paginate_detail_items(
360 filter: &IamResFilterReq,
361 page_number: u32,
362 page_size: u32,
363 desc_sort_by_create: Option<bool>,
364 desc_sort_by_update: Option<bool>,
365 funs: &TardisFunsInst,
366 ctx: &TardisContext,
367 ) -> TardisResult<TardisPage<IamResDetailResp>> {
368 let mut res = Self::do_paginate_detail_items(filter, page_number, page_size, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
369 res.records = res.records.into_iter().map(|r| r.decoding()).collect();
370 Ok(res)
371 }
372
373 async fn find_one_item(filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResSummaryResp>> {
374 let res = Self::do_find_one_item(filter, funs, ctx).await?;
375 if let Some(r) = res {
376 Ok(Some(r.decoding()))
377 } else {
378 Ok(None)
379 }
380 }
381
382 async fn find_items(
383 filter: &IamResFilterReq,
384 desc_sort_by_create: Option<bool>,
385 desc_sort_by_update: Option<bool>,
386 funs: &TardisFunsInst,
387 ctx: &TardisContext,
388 ) -> TardisResult<Vec<IamResSummaryResp>> {
389 let res = Self::do_find_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
390 Ok(res.into_iter().map(|r| r.decoding()).collect())
391 }
392
393 async fn find_one_detail_item(filter: &IamResFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamResDetailResp>> {
394 let res = Self::do_find_one_detail_item(filter, funs, ctx).await?;
395 if let Some(r) = res {
396 Ok(Some(r.decoding()))
397 } else {
398 Ok(None)
399 }
400 }
401
402 async fn find_detail_items(
403 filter: &IamResFilterReq,
404 desc_sort_by_create: Option<bool>,
405 desc_sort_by_update: Option<bool>,
406 funs: &TardisFunsInst,
407 ctx: &TardisContext,
408 ) -> TardisResult<Vec<IamResDetailResp>> {
409 let res = Self::do_find_detail_items(filter, desc_sort_by_create, desc_sort_by_update, funs, ctx).await?;
410 Ok(res.into_iter().map(|r| r.decoding()).collect())
411 }
412}
413
414impl IamResServ {
415 pub async fn find_from_id_rel_roles(
416 rel_kind: &IamRelKind,
417 with_sub: bool,
418 res_id: &str,
419 desc_by_create: Option<bool>,
420 desc_by_update: Option<bool>,
421 funs: &TardisFunsInst,
422 ctx: &TardisContext,
423 ) -> TardisResult<Vec<String>> {
424 IamRelServ::find_from_id_rels(rel_kind, with_sub, res_id, desc_by_create, desc_by_update, funs, ctx).await
425 }
426
427 pub async fn find_to_id_rel_roles(
428 rel_kind: &IamRelKind,
429 res_id: &str,
430 desc_by_create: Option<bool>,
431 desc_by_update: Option<bool>,
432 funs: &TardisFunsInst,
433 ctx: &TardisContext,
434 ) -> TardisResult<Vec<String>> {
435 IamRelServ::find_to_id_rels(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx).await
436 }
437
438 pub async fn find_from_simple_rel_roles(
439 rel_kind: &IamRelKind,
440 with_sub: bool,
441 res_id: &str,
442 desc_by_create: Option<bool>,
443 desc_by_update: Option<bool>,
444 funs: &TardisFunsInst,
445 ctx: &TardisContext,
446 ) -> TardisResult<Vec<RbumRelBoneResp>> {
447 IamRelServ::find_from_simple_rels(rel_kind, with_sub, res_id, desc_by_create, desc_by_update, funs, ctx).await
448 }
449
450 pub async fn find_to_simple_rel_roles(
451 rel_kind: &IamRelKind,
452 res_id: &str,
453 desc_by_create: Option<bool>,
454 desc_by_update: Option<bool>,
455 funs: &TardisFunsInst,
456 ctx: &TardisContext,
457 ) -> TardisResult<Vec<RbumRelBoneResp>> {
458 IamRelServ::find_to_simple_rels(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx).await
459 }
460
461 pub async fn find_rel_res(
462 rel_kind: &IamRelKind,
463 res_id: &str,
464 desc_by_create: Option<bool>,
465 desc_by_update: Option<bool>,
466 funs: &TardisFunsInst,
467 ctx: &TardisContext,
468 ) -> TardisResult<Vec<IamResDetailResp>> {
469 Ok(join_all(
470 Self::find_to_simple_rel_roles(rel_kind, res_id, desc_by_create, desc_by_update, funs, ctx)
471 .await?
472 .into_iter()
473 .map(|rel| async move { Self::get_item(&rel.rel_id, &IamResFilterReq::default(), funs, ctx).await.unwrap() })
474 .collect_vec(),
475 )
476 .await)
477 }
478
479 pub async fn find_to_multi_rel_roles(
480 rel_kind: &IamRelKind,
481 res_id: Vec<&str>,
482 funs: &TardisFunsInst,
483 ctx: &TardisContext,
484 ) -> TardisResult<HashMap<String, Vec<IamResDetailResp>>> {
485 let mut result = HashMap::new();
486 for id in res_id {
487 let res_list = join_all(
488 Self::find_to_simple_rel_roles(rel_kind, id, None, None, funs, ctx)
489 .await?
490 .into_iter()
491 .map(|rel| async move { Self::get_item(&rel.rel_id, &IamResFilterReq::default(), funs, ctx).await.unwrap() })
492 .collect_vec(),
493 )
494 .await;
495 result.insert(id.to_string(), res_list);
496 }
497 Ok(result)
498 }
499
500 pub async fn paginate_from_simple_rel_roles(
501 rel_kind: &IamRelKind,
502 res_id: &str,
503 with_sub: bool,
504 page_number: u32,
505 page_size: u32,
506 desc_by_create: Option<bool>,
507 desc_by_update: Option<bool>,
508 funs: &TardisFunsInst,
509 ctx: &TardisContext,
510 ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
511 IamRelServ::paginate_from_simple_rels(rel_kind, with_sub, res_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
512 }
513
514 pub async fn paginate_to_simple_rel_roles(
515 rel_kind: &IamRelKind,
516 res_id: &str,
517 page_number: u32,
518 page_size: u32,
519 desc_by_create: Option<bool>,
520 desc_by_update: Option<bool>,
521 funs: &TardisFunsInst,
522 ctx: &TardisContext,
523 ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
524 IamRelServ::paginate_to_simple_rels(rel_kind, res_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
525 }
526
527 pub async fn add_res_agg(add_req: &mut IamResAggAddReq, set_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
528 if add_req.res.kind == IamResKind::Menu {
529 let set_cate_sys_code_node_len = funs.rbum_conf_set_cate_sys_code_node_len();
530 let menu_ids = &Self::find_id_items(
532 &IamResFilterReq {
533 basic: RbumBasicFilterReq {
534 with_sub_own_paths: true,
535 ..Default::default()
536 },
537 kind: Some(IamResKind::Menu),
538 ..Default::default()
539 },
540 None,
541 None,
542 funs,
543 ctx,
544 )
545 .await?;
546 let count = RbumSetItemServ::count_rbums(
547 &RbumSetItemFilterReq {
548 basic: RbumBasicFilterReq {
549 with_sub_own_paths: true,
550 ..Default::default()
551 },
552 sys_code_query_kind: Some(RbumSetCateLevelQueryKind::Sub),
553 rel_rbum_set_cate_sys_codes: Some(vec![String::from_utf8(vec![b'0'; set_cate_sys_code_node_len])?]),
554 rel_rbum_item_ids: Some(menu_ids.iter().map(|id| id.to_string()).collect()),
555 rel_rbum_set_id: Some(set_id.to_string()),
556 rel_rbum_set_cate_ids: Some(vec![add_req.set.set_cate_id.to_string()]),
557 ..Default::default()
558 },
559 funs,
560 ctx,
561 )
562 .await?;
563 if count > 0 {
564 return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "conflict error", "409-iam-cate-menu-conflict"));
565 }
566 }
567 let res_id = Self::add_item(&mut add_req.res, funs, ctx).await?;
568 IamSetServ::add_set_item(
569 &IamSetItemAddReq {
570 set_id: set_id.to_string(),
571 set_cate_id: add_req.set.set_cate_id.to_string(),
572 sort: 0,
573 rel_rbum_item_id: res_id.clone(),
574 },
575 funs,
576 ctx,
577 )
578 .await?;
579 if let Some(bind_api_res) = &add_req.res.bind_api_res {
580 for api_id in bind_api_res {
581 IamRelServ::add_simple_rel(&IamRelKind::IamResApi, api_id, &res_id, None, None, false, false, funs, ctx).await?;
582 }
583 }
584 Ok(res_id)
585 }
586
587 pub async fn get_res_by_app_code(
588 app_ids: Vec<String>,
589 res_codes: Option<Vec<String>>,
590 funs: &TardisFunsInst,
591 ctx: &TardisContext,
592 ) -> TardisResult<HashMap<String, Vec<IamResSummaryResp>>> {
593 let raw_roles = IamAccountServ::find_simple_rel_roles(&ctx.owner, true, Some(true), None, funs, ctx).await?;
594 let mut roles: Vec<RbumRelBoneResp> = vec![];
595 let mut result = HashMap::new();
596 for role in raw_roles {
597 if !IamRoleServ::is_disabled(&role.rel_id, funs).await? {
598 roles.push(role)
599 }
600 }
601 let global_ctx = IamCertServ::use_sys_ctx_unsafe(ctx.clone())?;
602 for app_id in app_ids {
603 let mut res_ids = HashSet::new();
604 let app_ctx = IamCertServ::try_use_app_ctx(ctx.clone(), Some(app_id.clone()))?;
605 let app_role_ids =
606 roles.iter().filter(|r| r.rel_own_paths == app_ctx.own_paths || r.rel_own_paths == ctx.own_paths).map(|r| r.rel_id.to_string()).collect::<Vec<String>>();
607 res_ids.insert("".to_string());
609 for role_id in app_role_ids {
610 let rel_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, &role_id, None, None, funs, &global_ctx).await?;
611 res_ids.extend(rel_res_ids.into_iter());
612 if role_id.contains(':') {
613 let extend_role_id = role_id.split(':').collect::<Vec<&str>>()[0];
614 let rel_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, extend_role_id, None, None, funs, &global_ctx).await?;
615 res_ids.extend(rel_res_ids.into_iter());
616 }
617 }
618 let res_codes = if let Some(res_codes) = &res_codes {
619 let codes = res_codes.clone();
620 Some(res_codes.iter().map(|code| format!("{}/{}/{}", IamResKind::Ele.to_int(), "*", code)).chain(codes).collect::<Vec<String>>())
621 } else {
622 None
623 };
624 let res = Self::find_items(
625 &IamResFilterReq {
626 basic: RbumBasicFilterReq {
627 with_sub_own_paths: true,
628 ids: Some(res_ids.into_iter().collect()),
629 codes: res_codes.clone(),
630 ..Default::default()
631 },
632 ..Default::default()
633 },
634 None,
635 None,
636 funs,
637 ctx,
638 )
639 .await?;
640 result.insert(app_id, res);
641 }
642 Ok(result)
643 }
644
645 pub async fn delete_res(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
646 let item_detail = Self::get_item(
647 id,
648 &IamResFilterReq {
649 basic: RbumBasicFilterReq {
650 with_sub_own_paths: true,
651 ..Default::default()
652 },
653 ..Default::default()
654 },
655 funs,
656 ctx,
657 )
658 .await?;
659 if item_detail.kind == IamResKind::Ele || item_detail.kind == IamResKind::Menu {
660 let delete_api_res = IamResServ::find_to_simple_rel_roles(&IamRelKind::IamResApi, id, None, None, funs, ctx).await?.into_iter().map(|rel| rel.rel_id).collect_vec();
661 for delete_api_id in delete_api_res {
662 IamRelServ::delete_simple_rel(&IamRelKind::IamResApi, &delete_api_id, id, funs, ctx).await?;
663 }
664 }
665 Self::delete_item_with_all_rels(id, funs, ctx).await
666 }
667}
668
669impl IamMenuServ {
670 pub fn parse_menu<'a>(set_id: &'a str, parent_cate_id: &'a str, json_menu: JsonMenu, funs: &'a TardisFunsInst, ctx: &'a TardisContext) -> BoxFuture<'a, TardisResult<String>> {
671 async move {
672 let new_cate_id = Self::add_cate_menu(
673 set_id,
674 parent_cate_id,
675 &json_menu.name,
676 &json_menu.bus_code,
677 &IamSetCateKind::parse(&json_menu.ext)?,
678 funs,
679 ctx,
680 )
681 .await?;
682 if let Some(items) = json_menu.items {
683 for item in items {
684 Self::parse_item(set_id, &new_cate_id, item, funs, ctx).await?;
685 }
686 };
687 if let Some(children_menus) = json_menu.children {
688 for children_menu in children_menus {
689 Self::parse_menu(set_id, &new_cate_id, children_menu, funs, ctx).await?;
690 }
691 };
692 Ok(new_cate_id)
693 }
694 .boxed()
695 }
696
697 async fn add_cate_menu<'a>(
698 set_id: &str,
699 parent_cate_menu_id: &str,
700 name: &str,
701 bus_code: &str,
702 ext: &IamSetCateKind,
703 funs: &TardisFunsInst,
704 ctx: &TardisContext,
705 ) -> TardisResult<String> {
706 RbumSetCateServ::add_rbum(
707 &mut RbumSetCateAddReq {
708 name: TrimString(name.to_string()),
709 bus_code: TrimString(bus_code.to_string()),
710 icon: None,
711 sort: None,
712 ext: Some(ext.to_string()),
713 rbum_parent_cate_id: Some(parent_cate_menu_id.to_string()),
714 rel_rbum_set_id: set_id.to_string(),
715 scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
716 },
717 funs,
718 ctx,
719 )
720 .await
721 }
722
723 async fn parse_item(set_id: &str, cate_menu_id: &str, item: MenuItem, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
724 let id = match &item.kind as &str {
725 "Menu" => Self::add_menu_res(set_id, cate_menu_id, &item.name, &item.code, funs, ctx).await?,
726 "Ele" => Self::add_ele_res(set_id, cate_menu_id, &item.name, &item.code, funs, ctx).await?,
727 _ => {
728 warn!("item({},{}) have unsupported kind {} !", &item.name, &item.code, &item.kind);
729 "".to_string()
730 }
731 };
732 Ok(id)
733 }
734 async fn add_menu_res<'a>(set_id: &str, cate_menu_id: &str, name: &str, code: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
735 IamResServ::add_res_agg(
736 &mut IamResAggAddReq {
737 res: IamResAddReq {
738 code: TrimString(code.to_string()),
739 name: TrimString(name.to_string()),
740 kind: IamResKind::Menu,
741 icon: None,
742 sort: None,
743 method: None,
744 hide: None,
745 action: None,
746 scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
747 disabled: None,
748 crypto_req: None,
749 crypto_resp: None,
750 double_auth: None,
751 double_auth_msg: None,
752 need_login: None,
753 bind_api_res: None,
754 ext: None,
755 },
756 set: IamSetItemAggAddReq {
757 set_cate_id: cate_menu_id.to_string(),
758 },
759 },
760 set_id,
761 funs,
762 ctx,
763 )
764 .await
765 }
766
767 async fn add_ele_res<'a>(set_id: &str, cate_menu_id: &str, name: &str, code: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
768 IamResServ::add_res_agg(
769 &mut IamResAggAddReq {
770 res: IamResAddReq {
771 code: TrimString(code.to_string()),
772 name: TrimString(name.to_string()),
773 kind: IamResKind::Ele,
774 icon: None,
775 sort: None,
776 method: None,
777 hide: None,
778 action: None,
779 scope_level: Some(iam_constants::RBUM_SCOPE_LEVEL_GLOBAL),
780 disabled: None,
781 crypto_req: None,
782 crypto_resp: None,
783 double_auth: None,
784 double_auth_msg: None,
785 need_login: None,
786 bind_api_res: None,
787 ext: None,
788 },
789 set: IamSetItemAggAddReq {
790 set_cate_id: cate_menu_id.to_string(),
791 },
792 },
793 set_id,
794 funs,
795 ctx,
796 )
797 .await
798 }
799}