bios_iam/basic/serv/
iam_role_serv.rs

1use std::ops::Add;
2
3use async_trait::async_trait;
4use itertools::Itertools;
5use tardis::basic::dto::TardisContext;
6use tardis::basic::field::TrimString;
7use tardis::basic::result::TardisResult;
8use tardis::db::sea_orm::prelude::Expr;
9use tardis::db::sea_orm::sea_query::SelectStatement;
10use tardis::db::sea_orm::*;
11use tardis::log::info;
12use tardis::web::web_resp::TardisPage;
13use tardis::{tokio, TardisFuns, TardisFunsInst};
14
15use bios_basic::helper::request_helper::get_real_ip_from_ctx;
16use bios_basic::process::task_processor::TaskProcessor;
17use bios_basic::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumRelFilterReq};
18use bios_basic::rbum::dto::rbum_item_dto::{RbumItemKernelAddReq, RbumItemKernelModifyReq};
19use bios_basic::rbum::dto::rbum_rel_dto::{RbumRelBoneResp, RbumRelCheckReq};
20use bios_basic::rbum::helper::rbum_scope_helper;
21use bios_basic::rbum::helper::rbum_scope_helper::get_scope_level_by_context;
22use bios_basic::rbum::rbum_enumeration::{RbumRelFromKind, RbumScopeLevelKind};
23use bios_basic::rbum::serv::rbum_item_serv::RbumItemCrudOperation;
24use bios_basic::rbum::serv::rbum_rel_serv::RbumRelServ;
25
26use crate::basic::domain::iam_role;
27use crate::basic::dto::iam_filer_dto::{IamAppFilterReq, IamRoleFilterReq, IamTenantFilterReq};
28use crate::basic::dto::iam_role_dto::{IamRoleAddReq, IamRoleAggAddReq, IamRoleAggCopyReq, IamRoleAggModifyReq, IamRoleDetailResp, IamRoleModifyReq, IamRoleSummaryResp};
29use crate::basic::serv::iam_app_serv::IamAppServ;
30use crate::basic::serv::iam_key_cache_serv::IamIdentCacheServ;
31use crate::basic::serv::iam_rel_serv::IamRelServ;
32use crate::iam_config::{IamBasicConfigApi, IamBasicInfoManager, IamConfig};
33use crate::iam_constants::{self, IAM_AVATAR, RBUM_ITEM_ID_SUB_ROLE_LEN};
34use crate::iam_constants::{RBUM_SCOPE_LEVEL_APP, RBUM_SCOPE_LEVEL_TENANT};
35use crate::iam_enumeration::{IamRelKind, IamRoleKind};
36
37use super::clients::iam_kv_client::IamKvClient;
38use super::clients::iam_log_client::{IamLogClient, LogParamTag};
39use super::clients::iam_search_client::IamSearchClient;
40use super::iam_cert_serv::IamCertServ;
41use super::iam_tenant_serv::IamTenantServ;
42
43pub struct IamRoleServ;
44
45#[async_trait]
46impl RbumItemCrudOperation<iam_role::ActiveModel, IamRoleAddReq, IamRoleModifyReq, IamRoleSummaryResp, IamRoleDetailResp, IamRoleFilterReq> for IamRoleServ {
47    fn get_ext_table_name() -> &'static str {
48        iam_role::Entity.table_name()
49    }
50
51    fn get_rbum_kind_id() -> Option<String> {
52        Some(IamBasicInfoManager::get_config(|conf| conf.kind_role_id.clone()))
53    }
54
55    fn get_rbum_domain_id() -> Option<String> {
56        Some(IamBasicInfoManager::get_config(|conf| conf.domain_iam_id.clone()))
57    }
58
59    async fn package_item_add(add_req: &IamRoleAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<RbumItemKernelAddReq> {
60        Ok(RbumItemKernelAddReq {
61            id: if add_req.extend_role_id.is_some() {
62                Some(TrimString::from(format!(
63                    "{}:{}",
64                    add_req.extend_role_id.clone().unwrap_or_default(),
65                    Self::get_sub_new_id()
66                )))
67            } else {
68                None
69            },
70            code: add_req.code.clone(),
71            name: add_req.name.clone(),
72            scope_level: add_req.scope_level.clone(),
73            ..Default::default()
74        })
75    }
76
77    async fn package_ext_add(id: &str, add_req: &IamRoleAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<iam_role::ActiveModel> {
78        Ok(iam_role::ActiveModel {
79            id: Set(id.to_string()),
80            icon: Set(add_req.icon.as_ref().unwrap_or(&"".to_string()).to_string()),
81            sort: Set(add_req.sort.unwrap_or(0)),
82            kind: Set(add_req.kind.as_ref().unwrap_or(&IamRoleKind::Tenant).to_int()),
83            in_embed: Set(add_req.in_embed.unwrap_or(false)),
84            in_base: Set(add_req.in_base.unwrap_or(false)),
85            extend_role_id: Set(add_req.extend_role_id.as_ref().unwrap_or(&"".to_string()).to_string()),
86            ..Default::default()
87        })
88    }
89
90    async fn after_add_item(id: &str, _: &mut IamRoleAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
91        let role = Self::do_get_item(
92            id,
93            &IamRoleFilterReq {
94                basic: RbumBasicFilterReq {
95                    with_sub_own_paths: true,
96                    ..Default::default()
97                },
98                ..Default::default()
99            },
100            funs,
101            ctx,
102        )
103        .await?;
104        funs.cache()
105            .set(
106                &format!("{}{}", funs.conf::<IamConfig>().cache_key_role_info_, id),
107                TardisFuns::json.obj_to_string(&role)?.as_str(),
108            )
109            .await?;
110
111        let _ = IamLogClient::add_ctx_task(
112            LogParamTag::IamRole,
113            Some(id.to_string()),
114            "添加自定义角色".to_string(),
115            Some("AddCustomizeRole".to_string()),
116            ctx,
117        )
118        .await;
119        IamKvClient::async_add_or_modify_key_name(funs.conf::<IamConfig>().spi.kv_role_prefix.clone(), id.to_string(), role.name.clone(), funs, ctx).await?;
120
121        Ok(())
122    }
123
124    async fn package_item_modify(_: &str, modify_req: &IamRoleModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>> {
125        if modify_req.name.is_none() && modify_req.scope_level.is_none() && modify_req.disabled.is_none() {
126            return Ok(None);
127        }
128        Ok(Some(RbumItemKernelModifyReq {
129            code: None,
130            name: modify_req.name.clone(),
131            scope_level: modify_req.scope_level.clone(),
132            disabled: modify_req.disabled,
133        }))
134    }
135
136    async fn package_ext_modify(id: &str, modify_req: &IamRoleModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<iam_role::ActiveModel>> {
137        if modify_req.icon.is_none() && modify_req.sort.is_none() {
138            return Ok(None);
139        }
140        let mut iam_role = iam_role::ActiveModel {
141            id: Set(id.to_string()),
142            ..Default::default()
143        };
144        if let Some(icon) = &modify_req.icon {
145            iam_role.icon = Set(icon.to_string());
146        }
147        if let Some(sort) = modify_req.sort {
148            iam_role.sort = Set(sort);
149        }
150        if let Some(kind) = &modify_req.kind {
151            iam_role.kind = Set(kind.to_int());
152        }
153        Ok(Some(iam_role))
154    }
155
156    async fn after_modify_item(id: &str, modify_req: &mut IamRoleModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
157        let role = Self::do_get_item(
158            id,
159            &IamRoleFilterReq {
160                basic: RbumBasicFilterReq {
161                    with_sub_own_paths: true,
162                    ..Default::default()
163                },
164                ..Default::default()
165            },
166            funs,
167            ctx,
168        )
169        .await?;
170        funs.cache()
171            .set(
172                &format!("{}{}", funs.conf::<IamConfig>().cache_key_role_info_, id),
173                TardisFuns::json.obj_to_string(&role)?.as_str(),
174            )
175            .await?;
176        let role_id = id.to_string();
177        let ctx_clone = ctx.clone();
178        if modify_req.disabled.unwrap_or(false) {
179            TaskProcessor::execute_task_with_ctx(
180                &funs.conf::<IamConfig>().cache_key_async_task_status,
181                |_task_id| async move {
182                    let funs = iam_constants::get_tardis_inst();
183                    let mut count = IamRoleServ::count_rel_accounts(&role_id, &funs, &ctx_clone).await.unwrap_or_default() as isize;
184                    let mut page_number = 1;
185                    while count > 0 {
186                        let mut ids = Vec::new();
187                        if let Ok(page) = IamRoleServ::paginate_id_rel_accounts(&role_id, page_number, 100, None, None, &funs, &ctx_clone).await {
188                            ids = page.records;
189                        }
190                        for id in ids {
191                            IamIdentCacheServ::delete_tokens_and_contexts_by_account_id(&id, get_real_ip_from_ctx(&ctx_clone).await?, &funs).await?;
192                        }
193                        page_number += 1;
194                        count -= 100;
195                    }
196                    Ok(())
197                },
198                &funs.cache(),
199                IAM_AVATAR.to_owned(),
200                Some(vec![format!("account/{}", ctx.owner)]),
201                ctx,
202            )
203            .await?;
204        }
205
206        let mut op_describe = String::new();
207        let mut op_kind = String::new();
208        if modify_req.name.is_some() {
209            if Self::is_custom_role(role.kind, role.scope_level.clone()) {
210                op_describe = format!("编辑自定义角色名称为{}", modify_req.name.as_ref().unwrap_or(&TrimString::from("")));
211                op_kind = "ModifyCustomizeRoleName".to_string();
212            } else {
213                op_describe = format!("编辑内置角色名称为{}", modify_req.name.as_ref().unwrap_or(&TrimString::from("")));
214                op_kind = "ModifyBuiltRoleName".to_string();
215            }
216        }
217
218        if !op_describe.is_empty() {
219            let _ = IamLogClient::add_ctx_task(LogParamTag::IamRole, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
220        }
221        IamKvClient::async_add_or_modify_key_name(funs.conf::<IamConfig>().spi.kv_role_prefix.clone(), id.to_string(), role.name.clone(), funs, ctx).await?;
222
223        Ok(())
224    }
225
226    async fn before_delete_item(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<IamRoleDetailResp>> {
227        let item = IamRoleServ::get_item(
228            id,
229            &IamRoleFilterReq {
230                basic: RbumBasicFilterReq {
231                    ignore_scope: true,
232                    rel_ctx_owner: true,
233                    with_sub_own_paths: true,
234                    ..Default::default()
235                },
236                ..Default::default()
237            },
238            funs,
239            ctx,
240        )
241        .await?;
242        if item.scope_level != RbumScopeLevelKind::Private
243            || item.in_embed
244            || item.in_base
245            || id == funs.iam_basic_role_app_admin_id()
246            || id == funs.iam_basic_role_sys_admin_id()
247            || id == funs.iam_basic_role_tenant_admin_id()
248        {
249            return Err(funs.err().conflict(&Self::get_obj_name(), "delete", "role is not private", "409-iam-delete-role-conflict"));
250        }
251        let sub_role = Self::find_id_items(
252            &IamRoleFilterReq {
253                basic: RbumBasicFilterReq {
254                    with_sub_own_paths: true,
255                    ignore_scope: true,
256                    own_paths: Some("".to_string()),
257                    ..Default::default()
258                },
259                extend_role_id: Some(id.to_string()),
260                ..Default::default()
261            },
262            None,
263            None,
264            funs,
265            ctx,
266        )
267        .await?;
268        let ctx_clone = ctx.clone();
269        ctx.add_async_task(Box::new(|| {
270            Box::pin(async move {
271                let task_handle = tokio::spawn(async move {
272                    let funs = iam_constants::get_tardis_inst();
273                    for role_id in sub_role {
274                        let _ = Self::delete_item_with_all_rels(&role_id, &funs, &ctx_clone).await;
275                    }
276                });
277                task_handle.await.unwrap();
278                Ok(())
279            })
280        }))
281        .await?;
282        Ok(None)
283    }
284
285    async fn after_delete_item(id: &str, _: &Option<IamRoleDetailResp>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
286        funs.cache().del(&format!("{}{}", funs.conf::<IamConfig>().cache_key_role_info_, id)).await?;
287        let role_id = id.to_string();
288        let ctx_clone = ctx.clone();
289        // TODO 待优化 增加-缓存-角色与用户的关联关系 | 以便解决删除角色后,用户的token和context不会被删除的问题
290        // 现代码问题: 删除角色后,角色与用户的关联关系逻辑有冲突,导致用户的token和context不会被删除
291        TaskProcessor::execute_task_with_ctx(
292            &funs.conf::<IamConfig>().cache_key_async_task_status,
293            |_task_id| async move {
294                let funs = iam_constants::get_tardis_inst();
295                let mut count = IamRoleServ::count_rel_accounts(&role_id, &funs, &ctx_clone).await.unwrap_or_default() as isize;
296                let mut page_number = 1;
297                while count > 0 {
298                    let mut ids = Vec::new();
299                    if let Ok(page) = IamRoleServ::paginate_id_rel_accounts(&role_id, page_number, 100, None, None, &funs, &ctx_clone).await {
300                        ids = page.records;
301                    }
302                    for id in ids {
303                        IamIdentCacheServ::delete_tokens_and_contexts_by_account_id(&id, get_real_ip_from_ctx(&ctx_clone).await?, &funs).await?;
304                    }
305                    page_number += 1;
306                    count -= 100;
307                }
308                Ok(())
309            },
310            &funs.cache(),
311            IAM_AVATAR.to_owned(),
312            Some(vec![format!("account/{}", ctx.owner)]),
313            ctx,
314        )
315        .await?;
316
317        let _ = IamLogClient::add_ctx_task(
318            LogParamTag::IamRole,
319            Some(id.to_string()),
320            "删除自定义角色".to_string(),
321            Some("DeleteCustomizeRole".to_string()),
322            ctx,
323        )
324        .await;
325        IamKvClient::async_delete_key_name(funs.conf::<IamConfig>().spi.kv_role_prefix.clone(), id.to_string(), funs, ctx).await?;
326        Ok(())
327    }
328
329    async fn package_ext_query(query: &mut SelectStatement, _: bool, filter: &IamRoleFilterReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
330        query.column((iam_role::Entity, iam_role::Column::Icon));
331        query.column((iam_role::Entity, iam_role::Column::Sort));
332        query.column((iam_role::Entity, iam_role::Column::Kind));
333        query.column((iam_role::Entity, iam_role::Column::InBase));
334        query.column((iam_role::Entity, iam_role::Column::InEmbed));
335        query.column((iam_role::Entity, iam_role::Column::ExtendRoleId));
336        if let Some(kind) = &filter.kind {
337            query.and_where(Expr::col(iam_role::Column::Kind).eq(kind.to_int()));
338        }
339        if let Some(in_embed) = &filter.in_embed {
340            query.and_where(Expr::col(iam_role::Column::InEmbed).eq(*in_embed));
341        }
342        if let Some(in_base) = &filter.in_base {
343            query.and_where(Expr::col(iam_role::Column::InBase).eq(*in_base));
344        }
345        if let Some(extend_role_id) = &filter.extend_role_id {
346            query.and_where(Expr::col(iam_role::Column::ExtendRoleId).eq(extend_role_id));
347        }
348        Ok(())
349    }
350
351    async fn get_item(id: &str, filter: &IamRoleFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<IamRoleDetailResp> {
352        if let Some(role) = funs.cache().get(&format!("{}{}", funs.conf::<IamConfig>().cache_key_role_info_, id)).await? {
353            let role = TardisFuns::json.str_to_obj::<IamRoleDetailResp>(&role)?;
354            if rbum_scope_helper::check_scope(&role.own_paths, Some(role.scope_level.to_int()), &filter.basic, &ctx.own_paths) {
355                return Ok(role);
356            }
357        }
358        let role = Self::do_get_item(id, filter, funs, ctx).await?;
359        funs.cache()
360            .set(
361                &format!("{}{}", funs.conf::<IamConfig>().cache_key_role_info_, id),
362                TardisFuns::json.obj_to_string(&role)?.as_str(),
363            )
364            .await?;
365        Ok(role)
366    }
367}
368
369impl IamRoleServ {
370    pub fn get_sub_new_id() -> String {
371        TardisFuns::field.nanoid_len(RBUM_ITEM_ID_SUB_ROLE_LEN as usize)
372    }
373
374    pub async fn extend_copy_role_agg(
375        tenant_or_app_id: &str,
376        spec_role_ids: Option<Vec<String>>,
377        kind: &IamRoleKind,
378        funs: &TardisFunsInst,
379        ctx: &TardisContext,
380    ) -> TardisResult<()> {
381        let base_roles = Self::find_detail_items(
382            &IamRoleFilterReq {
383                basic: RbumBasicFilterReq {
384                    ids: spec_role_ids,
385                    ignore_scope: true,
386                    with_sub_own_paths: false,
387                    own_paths: Some("".to_string()),
388                    ..Default::default()
389                },
390                kind: Some(kind.clone()),
391                in_embed: Some(true),
392                in_base: Some(true),
393                ..Default::default()
394            },
395            None,
396            None,
397            funs,
398            ctx,
399        )
400        .await?;
401        for base_role in base_roles {
402            Self::add_role_agg(
403                &mut IamRoleAggAddReq {
404                    role: IamRoleAddReq {
405                        code: Some(TrimString::from(format!("{}:{}", tenant_or_app_id, base_role.code))),
406                        name: TrimString::from(base_role.name),
407                        icon: Some(base_role.icon),
408                        sort: Some(base_role.sort),
409                        kind: Some(base_role.kind),
410                        scope_level: Some(RbumScopeLevelKind::Private),
411                        in_embed: Some(base_role.in_embed),
412                        extend_role_id: Some(base_role.id),
413                        disabled: Some(base_role.disabled),
414                        in_base: Some(false),
415                    },
416                    res_ids: None,
417                },
418                funs,
419                ctx,
420            )
421            .await?;
422        }
423        Ok(())
424    }
425
426    pub async fn add_app_copy_role_agg(app_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
427        Self::extend_copy_role_agg(app_id, None, &IamRoleKind::App, funs, ctx).await?;
428        let tenant_ctx = IamCertServ::use_sys_or_tenant_ctx_unsafe(ctx.clone())?;
429        let tenant_app_roles = Self::find_detail_items(
430            &IamRoleFilterReq {
431                basic: RbumBasicFilterReq { ..Default::default() },
432                kind: Some(IamRoleKind::App),
433                in_embed: Some(false),
434                in_base: Some(false),
435                ..Default::default()
436            },
437            None,
438            None,
439            funs,
440            &tenant_ctx,
441        )
442        .await?;
443        for app_role in tenant_app_roles {
444            Self::add_role_agg(
445                &mut IamRoleAggAddReq {
446                    role: IamRoleAddReq {
447                        code: Some(TrimString::from(format!("{}:{}", app_id, app_role.code))),
448                        name: TrimString::from(app_role.name),
449                        icon: Some(app_role.icon),
450                        sort: Some(app_role.sort),
451                        kind: Some(app_role.kind),
452                        scope_level: Some(RbumScopeLevelKind::Private),
453                        in_embed: Some(app_role.in_embed),
454                        extend_role_id: Some(app_role.id),
455                        disabled: Some(app_role.disabled),
456                        in_base: Some(false),
457                    },
458                    res_ids: None,
459                },
460                funs,
461                ctx,
462            )
463            .await?;
464        }
465        Ok(())
466    }
467
468    pub async fn get_embed_sub_role_id(extend_role_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
469        let scope_level = get_scope_level_by_context(ctx)?;
470        info!(
471            "【get_embed_subrole_id】 : extend_role_id = {}, scope_level = {}, own_paths = {}",
472            extend_role_id, scope_level, ctx.own_paths
473        );
474        let kind = if scope_level == RBUM_SCOPE_LEVEL_APP { IamRoleKind::App } else { IamRoleKind::Tenant };
475        if let Some(base_role) = Self::find_one_item(
476            &IamRoleFilterReq {
477                kind: Some(kind),
478                // in_embed: Some(true),
479                extend_role_id: Some(extend_role_id.to_string()),
480                ..Default::default()
481            },
482            funs,
483            ctx,
484        )
485        .await?
486        {
487            return Ok(base_role.id);
488        }
489        Err(funs.err().not_found(&Self::get_obj_name(), "get_embed_subrole_id", "role not found", "404-iam-role-not-found"))
490    }
491
492    /// 租户添加应用角色
493    pub async fn tenant_add_app_role_agg(add_req: &mut IamRoleAggAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
494        add_req.role.scope_level = Some(RbumScopeLevelKind::Private);
495        let app_role_id = Self::add_role_agg(add_req, funs, ctx).await?;
496        let app_ids = IamAppServ::find_id_items(
497            &IamAppFilterReq {
498                basic: RbumBasicFilterReq {
499                    with_sub_own_paths: true,
500                    ..Default::default()
501                },
502                ..Default::default()
503            },
504            None,
505            None,
506            funs,
507            ctx,
508        )
509        .await?;
510        let app_role = Self::get_item(
511            &app_role_id,
512            &IamRoleFilterReq {
513                basic: RbumBasicFilterReq {
514                    with_sub_own_paths: true,
515                    ..Default::default()
516                },
517                ..Default::default()
518            },
519            funs,
520            ctx,
521        )
522        .await?;
523        for app_id in app_ids {
524            let app_ctx = IamCertServ::try_use_app_ctx(ctx.clone(), Some(app_id.clone()))?;
525            Self::add_role_agg(
526                &mut IamRoleAggAddReq {
527                    role: IamRoleAddReq {
528                        code: Some(TrimString::from(format!("{}:{}", app_id, app_role.code))),
529                        name: TrimString::from(app_role.name.clone()),
530                        icon: Some(app_role.icon.clone()),
531                        sort: Some(app_role.sort),
532                        kind: Some(app_role.kind.clone()),
533                        scope_level: Some(RbumScopeLevelKind::Private),
534                        in_embed: Some(app_role.in_embed),
535                        extend_role_id: Some(app_role_id.clone()),
536                        disabled: Some(app_role.disabled),
537                        in_base: Some(false),
538                    },
539                    res_ids: None,
540                },
541                funs,
542                &app_ctx,
543            )
544            .await?;
545        }
546        Ok(app_role_id)
547    }
548
549    /// 复制租户添加应用角色
550    pub async fn copy_tenant_add_app_role_agg(copy_req: &mut IamRoleAggCopyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
551        copy_req.role.scope_level = Some(RbumScopeLevelKind::Private);
552        copy_req.sync_account = Some(false);
553        let app_role_id = Self::copy_role_agg(copy_req, funs, ctx).await?;
554        let app_ids = IamAppServ::find_id_items(
555            &IamAppFilterReq {
556                basic: RbumBasicFilterReq {
557                    with_sub_own_paths: true,
558                    ..Default::default()
559                },
560                ..Default::default()
561            },
562            None,
563            None,
564            funs,
565            ctx,
566        )
567        .await?;
568        let app_role = Self::get_item(
569            &app_role_id,
570            &IamRoleFilterReq {
571                basic: RbumBasicFilterReq {
572                    with_sub_own_paths: true,
573                    ..Default::default()
574                },
575                ..Default::default()
576            },
577            funs,
578            ctx,
579        )
580        .await?;
581        for app_id in app_ids {
582            let app_ctx = IamCertServ::try_use_app_ctx(ctx.clone(), Some(app_id.clone()))?;
583            Self::add_role_agg(
584                &mut IamRoleAggAddReq {
585                    role: IamRoleAddReq {
586                        code: Some(TrimString::from(format!("{}:{}", app_id, app_role.code))),
587                        name: TrimString::from(app_role.name.clone()),
588                        icon: Some(app_role.icon.clone()),
589                        sort: Some(app_role.sort),
590                        kind: Some(app_role.kind.clone()),
591                        scope_level: Some(RbumScopeLevelKind::Private),
592                        in_embed: Some(app_role.in_embed),
593                        extend_role_id: Some(app_role_id.clone()),
594                        disabled: Some(app_role.disabled),
595                        in_base: Some(false),
596                    },
597                    res_ids: None,
598                },
599                funs,
600                &app_ctx,
601            )
602            .await?;
603        }
604        Ok(app_role_id)
605    }
606
607    pub async fn add_role_agg(add_req: &mut IamRoleAggAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
608        let role_id = Self::add_item(&mut add_req.role, funs, ctx).await?;
609        if let Some(res_ids) = &add_req.res_ids {
610            for res_id in res_ids {
611                Self::add_rel_res(&role_id, res_id, funs, ctx).await?;
612            }
613        }
614        Ok(role_id)
615    }
616
617    pub async fn copy_role_agg(copy_req: &mut IamRoleAggCopyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
618        let copy_role = Self::get_item(
619            &copy_req.copy_role_id,
620            &IamRoleFilterReq {
621                basic: RbumBasicFilterReq {
622                    with_sub_own_paths: true,
623                    ..Default::default()
624                },
625                ..Default::default()
626            },
627            funs,
628            ctx,
629        )
630        .await?;
631        let role_id = Self::add_item(&mut copy_req.role, funs, ctx).await?;
632        Self::copy_rel_res(&role_id, &copy_role.id, funs, ctx).await?;
633        if copy_req.sync_account.unwrap_or(false) {
634            Self::copy_rel_account(&role_id, &copy_role.id, None, funs, ctx).await?;
635        }
636        Ok(role_id)
637    }
638
639    pub async fn copy_rel_res(role_id: &str, copy_role_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
640        let res_ids = Self::find_id_rel_res(copy_role_id, None, None, funs, ctx).await?;
641        for res_id in res_ids {
642            Self::add_rel_res(role_id, &res_id, funs, ctx).await?;
643        }
644        Ok(())
645    }
646
647    pub async fn copy_rel_account(role_id: &str, copy_role_id: &str, spec_scope_level: Option<RbumScopeLevelKind>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
648        let account_ids = Self::find_id_rel_accounts(copy_role_id, None, None, funs, ctx).await?;
649        for account_id in account_ids {
650            Self::add_rel_account(role_id, &account_id, spec_scope_level.clone(), funs, ctx).await?;
651        }
652        Ok(())
653    }
654
655    pub async fn modify_role_agg(id: &str, modify_req: &mut IamRoleAggModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
656        if let Some(role) = modify_req.role.as_mut() {
657            Self::modify_item(id, role, funs, ctx).await?;
658            if let Some(name) = &role.name {
659                let sub_role = Self::find_id_items(
660                    &IamRoleFilterReq {
661                        basic: RbumBasicFilterReq {
662                            with_sub_own_paths: true,
663                            ignore_scope: true,
664                            own_paths: Some("".to_string()),
665                            ..Default::default()
666                        },
667                        extend_role_id: Some(id.to_string()),
668                        ..Default::default()
669                    },
670                    None,
671                    None,
672                    funs,
673                    ctx,
674                )
675                .await?;
676                let ctx_clone = ctx.clone();
677                let name_clone = name.clone();
678                ctx.add_async_task(Box::new(|| {
679                    Box::pin(async move {
680                        let task_handle = tokio::spawn(async move {
681                            let funs = iam_constants::get_tardis_inst();
682                            for role_id in sub_role {
683                                let _ = Self::modify_item(
684                                    &role_id,
685                                    &mut IamRoleModifyReq {
686                                        name: Some(name_clone.clone()),
687                                        ..Default::default()
688                                    },
689                                    &funs,
690                                    &ctx_clone,
691                                )
692                                .await;
693                            }
694                        });
695                        task_handle.await.unwrap();
696                        Ok(())
697                    })
698                }))
699                .await?;
700            }
701        }
702        if let Some(input_res_ids) = &modify_req.res_ids {
703            let stored_res = Self::find_simple_rel_res(id, None, None, funs, ctx).await?;
704            let stored_res_ids: Vec<String> = stored_res.into_iter().map(|x| x.rel_id).collect();
705            for input_res_id in input_res_ids {
706                if !stored_res_ids.contains(input_res_id) {
707                    Self::add_rel_res(id, input_res_id, funs, ctx).await?;
708                }
709            }
710            for stored_res_id in stored_res_ids {
711                if !input_res_ids.contains(&stored_res_id) {
712                    Self::delete_rel_res(id, &stored_res_id, funs, ctx).await?;
713                }
714            }
715
716            let role = Self::do_get_item(
717                id,
718                &IamRoleFilterReq {
719                    basic: RbumBasicFilterReq {
720                        with_sub_own_paths: true,
721                        ..Default::default()
722                    },
723                    ..Default::default()
724                },
725                funs,
726                ctx,
727            )
728            .await?;
729
730            let (op_describe, op_kind) = if Self::is_custom_role(role.kind, role.scope_level) {
731                ("编辑自定义角色权限".to_string(), "ModifyCustomizeRolePermissions".to_string())
732            } else {
733                ("编辑内置角色权限".to_string(), "ModifyBuiltRolePermissions".to_string())
734            };
735            let _ = IamLogClient::add_ctx_task(LogParamTag::IamRole, Some(id.to_string()), op_describe, Some(op_kind), ctx).await;
736        }
737        Ok(())
738    }
739
740    pub async fn add_rel_account(role_id: &str, account_id: &str, spec_scope_level: Option<RbumScopeLevelKind>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
741        let scope_level = get_scope_level_by_context(ctx)?;
742        let sub_tenant_admin_role_id = match scope_level {
743            RBUM_SCOPE_LEVEL_APP => Self::get_embed_sub_role_id(&funs.iam_basic_role_tenant_admin_id(), funs, &IamCertServ::use_sys_or_tenant_ctx_unsafe(ctx.clone())?).await?,
744            RBUM_SCOPE_LEVEL_TENANT => Self::get_embed_sub_role_id(&funs.iam_basic_role_tenant_admin_id(), funs, ctx).await?,
745            _ => "".to_string(),
746        };
747        if scope_level == RBUM_SCOPE_LEVEL_APP
748            && (role_id == funs.iam_basic_role_sys_admin_id() || role_id == funs.iam_basic_role_tenant_admin_id() || sub_tenant_admin_role_id == role_id)
749            || scope_level == RBUM_SCOPE_LEVEL_TENANT && role_id == funs.iam_basic_role_sys_admin_id()
750        {
751            return Err(funs.err().conflict(&Self::get_obj_name(), "add_rel_account", "associated role is invalid", "409-iam-role-rel-conflict"));
752        }
753
754        match Self::get_embed_sub_role_id(role_id, funs, ctx).await {
755            Ok(sub_role_id) => {
756                if let Some(spec_scope_level) = spec_scope_level {
757                    let role = Self::peek_item(&sub_role_id, &IamRoleFilterReq::default(), funs, ctx).await?;
758                    // The role is not private and current scope
759                    if role.scope_level != RbumScopeLevelKind::Private && role.scope_level.to_int() < spec_scope_level.to_int() {
760                        return Err(funs.err().conflict(&Self::get_obj_name(), "add_rel_account", "associated role is invalid", "409-iam-role-rel-conflict"));
761                    }
762                }
763                IamRelServ::add_simple_rel(&IamRelKind::IamAccountRole, account_id, &sub_role_id, None, None, true, false, funs, ctx).await?;
764            }
765            Err(_) => {
766                if let Some(spec_scope_level) = spec_scope_level {
767                    let role = Self::peek_item(role_id, &IamRoleFilterReq::default(), funs, ctx).await?;
768                    // The role is not private and current scope
769                    if role.scope_level != RbumScopeLevelKind::Private && role.scope_level.to_int() < spec_scope_level.to_int() {
770                        return Err(funs.err().conflict(&Self::get_obj_name(), "add_rel_account", "associated role is invalid", "409-iam-role-rel-conflict"));
771                    }
772                }
773                // TODO only bind the same own_paths roles
774                // E.g. sys admin can't bind tenant admin
775                IamRelServ::add_simple_rel(&IamRelKind::IamAccountRole, account_id, role_id, None, None, true, false, funs, ctx).await?;
776            }
777        }
778        IamSearchClient::async_add_or_modify_account_search(account_id, Box::new(true), "", funs, ctx).await?;
779        Ok(())
780    }
781
782    pub async fn delete_rel_account(role_id: &str, account_id: &str, spec_scope_level: Option<RbumScopeLevelKind>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
783        if let Some(spec_scope_level) = spec_scope_level {
784            let role = Self::peek_item(role_id, &IamRoleFilterReq::default(), funs, ctx).await?;
785            // The role is not private and current scope
786            if role.scope_level != RbumScopeLevelKind::Private && role.scope_level != spec_scope_level {
787                return Err(funs.err().conflict(&Self::get_obj_name(), "delete_rel_account", "associated role is invalid", "409-iam-role-rel-conflict"));
788            }
789        }
790        let scope_level = get_scope_level_by_context(ctx)?;
791        let sub_role_id = match scope_level {
792            RBUM_SCOPE_LEVEL_APP => Self::get_embed_sub_role_id(&funs.iam_basic_role_app_admin_id(), funs, ctx).await?,
793            RBUM_SCOPE_LEVEL_TENANT => Self::get_embed_sub_role_id(&funs.iam_basic_role_tenant_admin_id(), funs, ctx).await?,
794            _ => "".to_string(),
795        };
796        if funs.iam_basic_role_sys_admin_id() == role_id
797            || funs.iam_basic_role_tenant_admin_id() == role_id
798            || funs.iam_basic_role_app_admin_id() == role_id
799            || sub_role_id == role_id
800        {
801            let count = IamRelServ::count_to_rels(&IamRelKind::IamAccountRole, role_id, funs, ctx).await?;
802            if count == 1 {
803                return Err(funs.err().conflict(
804                    &Self::get_obj_name(),
805                    "delete_rel_account",
806                    "the current role has only one user and cannot be deleted",
807                    "409-iam-role-del-only-one-user",
808                ));
809            }
810        }
811        match Self::get_embed_sub_role_id(role_id, funs, ctx).await {
812            Ok(sub_role_id) => {
813                IamRelServ::delete_simple_rel(&IamRelKind::IamAccountRole, account_id, &sub_role_id, funs, ctx).await?;
814            }
815            Err(_) => {
816                IamRelServ::delete_simple_rel(&IamRelKind::IamAccountRole, account_id, role_id, funs, ctx).await?;
817            }
818        }
819        IamSearchClient::async_add_or_modify_account_search(account_id, Box::new(true), "", funs, ctx).await?;
820        Ok(())
821    }
822
823    pub async fn count_rel_accounts(role_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
824        IamRelServ::count_to_rels(&IamRelKind::IamAccountRole, role_id, funs, ctx).await
825    }
826
827    pub async fn find_id_rel_accounts(
828        role_id: &str,
829        desc_by_create: Option<bool>,
830        desc_by_update: Option<bool>,
831        funs: &TardisFunsInst,
832        ctx: &TardisContext,
833    ) -> TardisResult<Vec<String>> {
834        IamRelServ::find_to_id_rels(&IamRelKind::IamAccountRole, role_id, desc_by_create, desc_by_update, funs, ctx).await
835    }
836
837    pub async fn find_simple_rel_accounts(
838        role_id: &str,
839        desc_by_create: Option<bool>,
840        desc_by_update: Option<bool>,
841        funs: &TardisFunsInst,
842        ctx: &TardisContext,
843    ) -> TardisResult<Vec<RbumRelBoneResp>> {
844        IamRelServ::find_to_simple_rels(&IamRelKind::IamAccountRole, role_id, desc_by_create, desc_by_update, funs, ctx).await
845    }
846
847    pub async fn paginate_id_rel_accounts(
848        role_id: &str,
849        page_number: u32,
850        page_size: u32,
851        desc_by_create: Option<bool>,
852        desc_by_update: Option<bool>,
853        funs: &TardisFunsInst,
854        ctx: &TardisContext,
855    ) -> TardisResult<TardisPage<String>> {
856        IamRelServ::paginate_to_id_rels(&IamRelKind::IamAccountRole, role_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
857    }
858
859    pub async fn paginate_simple_rel_accounts(
860        role_id: &str,
861        page_number: u32,
862        page_size: u32,
863        desc_by_create: Option<bool>,
864        desc_by_update: Option<bool>,
865        funs: &TardisFunsInst,
866        ctx: &TardisContext,
867    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
868        IamRelServ::paginate_to_simple_rels(&IamRelKind::IamAccountRole, role_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
869    }
870
871    pub async fn save_rel_res(role_id: &str, res_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
872        IamRelServ::add_simple_rel(&IamRelKind::IamResRole, res_id, role_id, None, None, false, false, funs, ctx).await?;
873        Ok(())
874    }
875
876    pub async fn add_rel_res(role_id: &str, res_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
877        IamRelServ::add_simple_rel(&IamRelKind::IamResRole, res_id, role_id, None, None, false, false, funs, ctx).await?;
878        Ok(())
879    }
880
881    pub async fn delete_rel_res(role_id: &str, res_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
882        IamRelServ::delete_simple_rel(&IamRelKind::IamResRole, res_id, role_id, funs, ctx).await?;
883        Ok(())
884    }
885
886    pub async fn count_rel_res(role_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<u64> {
887        let mut count = IamRelServ::count_to_rels(&IamRelKind::IamResRole, role_id, funs, ctx).await?;
888        let role = Self::get_item(
889            role_id,
890            &IamRoleFilterReq {
891                basic: RbumBasicFilterReq {
892                    own_paths: Some("".to_string()),
893                    with_sub_own_paths: true,
894                    ..Default::default()
895                },
896                ..Default::default()
897            },
898            funs,
899            ctx,
900        )
901        .await?;
902        if !role.extend_role_id.is_empty() {
903            let moke_ctx = TardisContext {
904                own_paths: "".to_string(),
905                ..ctx.clone()
906            };
907            let extend_count = IamRelServ::count_to_rels(&IamRelKind::IamResRole, &role.extend_role_id, funs, &moke_ctx).await?;
908            count = count.add(extend_count);
909        }
910        Ok(count)
911    }
912
913    pub async fn find_id_rel_res(
914        role_id: &str,
915        desc_by_create: Option<bool>,
916        desc_by_update: Option<bool>,
917        funs: &TardisFunsInst,
918        ctx: &TardisContext,
919    ) -> TardisResult<Vec<String>> {
920        let res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, role_id, desc_by_create, desc_by_update, funs, ctx).await?;
921        let role = Self::get_item(
922            role_id,
923            &IamRoleFilterReq {
924                basic: RbumBasicFilterReq {
925                    own_paths: Some("".to_string()),
926                    with_sub_own_paths: true,
927                    ..Default::default()
928                },
929                ..Default::default()
930            },
931            funs,
932            ctx,
933        )
934        .await?;
935        if !role.extend_role_id.is_empty() {
936            let moke_ctx = TardisContext {
937                own_paths: "".to_string(),
938                ..ctx.clone()
939            };
940            let extend_res_ids = IamRelServ::find_to_id_rels(&IamRelKind::IamResRole, &role.extend_role_id, desc_by_create, desc_by_update, funs, &moke_ctx).await?;
941            Ok([res_ids, extend_res_ids].concat())
942        } else {
943            Ok(res_ids)
944        }
945    }
946
947    pub async fn find_simple_rel_res(
948        role_id: &str,
949        desc_by_create: Option<bool>,
950        desc_by_update: Option<bool>,
951        funs: &TardisFunsInst,
952        ctx: &TardisContext,
953    ) -> TardisResult<Vec<RbumRelBoneResp>> {
954        let res = IamRelServ::find_to_simple_rels(&IamRelKind::IamResRole, role_id, desc_by_create, desc_by_update, funs, ctx).await?;
955        let role = Self::get_item(
956            role_id,
957            &IamRoleFilterReq {
958                basic: RbumBasicFilterReq {
959                    own_paths: Some("".to_string()),
960                    with_sub_own_paths: true,
961                    ..Default::default()
962                },
963                ..Default::default()
964            },
965            funs,
966            ctx,
967        )
968        .await?;
969        if !role.extend_role_id.is_empty() {
970            let moke_ctx = TardisContext {
971                own_paths: "".to_string(),
972                ..ctx.clone()
973            };
974            let extend_res = IamRelServ::find_to_simple_rels(&IamRelKind::IamResRole, &role.extend_role_id, desc_by_create, desc_by_update, funs, &moke_ctx).await?;
975            Ok([res, extend_res].concat())
976        } else {
977            Ok(res)
978        }
979    }
980
981    pub async fn find_simple_rels(
982        role_id: &str,
983        desc_sort_by_create: Option<bool>,
984        desc_sort_by_update: Option<bool>,
985        from_scope_levels: Option<Vec<i16>>,
986        to_scope_levels: Option<Vec<i16>>,
987        funs: &TardisFunsInst,
988        ctx: &TardisContext,
989    ) -> TardisResult<Vec<RbumRelBoneResp>> {
990        let res = IamRelServ::find_simple_rels(
991            &RbumRelFilterReq {
992                basic: RbumBasicFilterReq {
993                    own_paths: Some(ctx.own_paths.to_string()),
994                    with_sub_own_paths: true,
995                    ignore_scope: true,
996                    ..Default::default()
997                },
998                tag: Some(IamRelKind::IamResRole.to_string()),
999                to_rbum_item_id: Some(role_id.to_string()),
1000                from_rbum_scope_levels: from_scope_levels.clone(),
1001                to_rbum_item_scope_levels: to_scope_levels.clone(),
1002                ..Default::default()
1003            },
1004            desc_sort_by_create,
1005            desc_sort_by_update,
1006            false,
1007            funs,
1008            ctx,
1009        )
1010        .await?;
1011        let role = Self::get_item(
1012            role_id,
1013            &IamRoleFilterReq {
1014                basic: RbumBasicFilterReq {
1015                    own_paths: Some("".to_string()),
1016                    with_sub_own_paths: true,
1017                    ..Default::default()
1018                },
1019                ..Default::default()
1020            },
1021            funs,
1022            ctx,
1023        )
1024        .await?;
1025        if !role.extend_role_id.is_empty() {
1026            let extend_res = IamRelServ::find_simple_rels(
1027                &RbumRelFilterReq {
1028                    basic: RbumBasicFilterReq {
1029                        own_paths: Some(ctx.own_paths.to_string()),
1030                        with_sub_own_paths: true,
1031                        ignore_scope: true,
1032                        ..Default::default()
1033                    },
1034                    tag: Some(IamRelKind::IamResRole.to_string()),
1035                    to_rbum_item_id: Some(role.extend_role_id.to_string()),
1036                    from_rbum_scope_levels: from_scope_levels.clone(),
1037                    to_rbum_item_scope_levels: to_scope_levels.clone(),
1038                    ..Default::default()
1039                },
1040                desc_sort_by_create,
1041                desc_sort_by_update,
1042                false,
1043                funs,
1044                ctx,
1045            )
1046            .await?;
1047            Ok([res, extend_res].concat())
1048        } else {
1049            Ok(res)
1050        }
1051    }
1052
1053    pub async fn paginate_id_rel_res(
1054        role_id: &str,
1055        page_number: u32,
1056        page_size: u32,
1057        desc_by_create: Option<bool>,
1058        desc_by_update: Option<bool>,
1059        funs: &TardisFunsInst,
1060        ctx: &TardisContext,
1061    ) -> TardisResult<TardisPage<String>> {
1062        IamRelServ::paginate_to_id_rels(&IamRelKind::IamResRole, role_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
1063    }
1064
1065    pub async fn paginate_simple_rel_res(
1066        role_id: &str,
1067        page_number: u32,
1068        page_size: u32,
1069        desc_by_create: Option<bool>,
1070        desc_by_update: Option<bool>,
1071        funs: &TardisFunsInst,
1072        ctx: &TardisContext,
1073    ) -> TardisResult<TardisPage<RbumRelBoneResp>> {
1074        IamRelServ::paginate_to_simple_rels(&IamRelKind::IamResRole, role_id, page_number, page_size, desc_by_create, desc_by_update, funs, ctx).await
1075    }
1076
1077    pub async fn need_sys_admin(funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1078        Self::need_role(&funs.iam_basic_role_sys_admin_id(), funs, ctx).await
1079    }
1080
1081    pub async fn need_tenant_admin(funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1082        Self::need_role(&funs.iam_basic_role_tenant_admin_id(), funs, ctx).await
1083    }
1084
1085    pub async fn need_app_admin(funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1086        Self::need_role(&funs.iam_basic_role_app_admin_id(), funs, ctx).await
1087    }
1088
1089    pub async fn need_role(role_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1090        let exist = RbumRelServ::check_rel(
1091            &RbumRelCheckReq {
1092                tag: IamRelKind::IamAccountRole.to_string(),
1093                from_rbum_kind: RbumRelFromKind::Item,
1094                from_rbum_id: ctx.owner.clone(),
1095                to_rbum_item_id: role_id.to_string(),
1096                from_attrs: Default::default(),
1097                to_attrs: Default::default(),
1098                envs: Default::default(),
1099            },
1100            funs,
1101            ctx,
1102        )
1103        .await?;
1104        if !exist {
1105            Err(funs.err().unauthorized(&Self::get_obj_name(), "need_role", "illegal operation", "401-iam-role-illegal"))
1106        } else {
1107            Ok(())
1108        }
1109    }
1110
1111    pub fn is_custom_role(kind: IamRoleKind, scope_level: RbumScopeLevelKind) -> bool {
1112        kind != IamRoleKind::System && scope_level == RbumScopeLevelKind::Private
1113    }
1114
1115    pub async fn find_name_by_ids(ids: Vec<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Vec<String>> {
1116        Self::find_items(
1117            &IamRoleFilterReq {
1118                basic: RbumBasicFilterReq {
1119                    ids: Some(ids),
1120                    with_sub_own_paths: true,
1121                    own_paths: Some("".to_string()),
1122                    ..Default::default()
1123                },
1124                ..Default::default()
1125            },
1126            None,
1127            None,
1128            funs,
1129            ctx,
1130        )
1131        .await
1132        .map(|r| r.into_iter().map(|r| format!("{},{}", r.id, r.name)).collect())
1133    }
1134
1135    pub async fn add_base_embed_role(add_req: &IamRoleAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1136        let base_embed_role_id = Self::add_role_agg(
1137            &mut IamRoleAggAddReq {
1138                role: add_req.clone(),
1139                res_ids: None,
1140            },
1141            funs,
1142            ctx,
1143        )
1144        .await?;
1145        if let Some(kind) = &add_req.kind {
1146            let tenant_or_app_ids = match kind {
1147                IamRoleKind::System => None,
1148                IamRoleKind::Tenant => Some(
1149                    IamTenantServ::find_detail_items(
1150                        &IamTenantFilterReq {
1151                            basic: RbumBasicFilterReq {
1152                                with_sub_own_paths: true,
1153                                ..Default::default()
1154                            },
1155                            ..Default::default()
1156                        },
1157                        None,
1158                        None,
1159                        funs,
1160                        ctx,
1161                    )
1162                    .await?
1163                    .into_iter()
1164                    .map(|tenant| (tenant.id, tenant.own_paths))
1165                    .collect_vec(),
1166                ),
1167                IamRoleKind::App => Some(
1168                    IamAppServ::find_detail_items(
1169                        &IamAppFilterReq {
1170                            basic: RbumBasicFilterReq {
1171                                with_sub_own_paths: true,
1172                                enabled: Some(true),
1173                                ..Default::default()
1174                            },
1175                            ..Default::default()
1176                        },
1177                        None,
1178                        None,
1179                        funs,
1180                        ctx,
1181                    )
1182                    .await?
1183                    .into_iter()
1184                    .map(|app| (app.id, app.own_paths))
1185                    .collect_vec(),
1186                ),
1187            };
1188            if let Some(tenant_or_app_ids) = tenant_or_app_ids {
1189                for (tenant_or_app_id, own_paths) in tenant_or_app_ids {
1190                    let tenant_or_app_ctx = TardisContext { own_paths, ..ctx.clone() };
1191                    Self::extend_copy_role_agg(&tenant_or_app_id, Some(vec![base_embed_role_id.clone()]), kind, funs, &tenant_or_app_ctx).await?;
1192                }
1193            }
1194        }
1195        Ok(())
1196    }
1197}