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 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 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 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 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 ©_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, ©_role.id, funs, ctx).await?;
633 if copy_req.sync_account.unwrap_or(false) {
634 Self::copy_rel_account(&role_id, ©_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 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 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 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 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}