1use std::default::Default;
2use std::str::FromStr;
3
4use bios_basic::helper::request_helper::{add_ip, get_real_ip_from_ctx};
5use bios_basic::process::task_processor::TaskProcessor;
6use bios_basic::rbum::rbum_config::RbumConfigApi;
7use bios_basic::rbum::serv::rbum_crud_serv::RbumCrudOperation;
8use itertools::Itertools;
9use serde::{Deserialize, Serialize};
10use tardis::basic::dto::TardisContext;
11use tardis::basic::result::TardisResult;
12use tardis::chrono::Utc;
13use tardis::{log, TardisFuns, TardisFunsInst};
14
15use bios_basic::rbum::dto::rbum_filer_dto::RbumBasicFilterReq;
16use bios_basic::rbum::serv::rbum_item_serv::{RbumItemCrudOperation, RbumItemServ};
17
18use crate::basic::dto::iam_account_dto::IamAccountInfoResp;
19use crate::basic::dto::iam_cert_dto::IamContextFetchReq;
20use crate::basic::dto::iam_filer_dto::{IamAccountFilterReq, IamAppFilterReq};
21use crate::basic::serv::clients::iam_log_client::{IamLogClient, LogParamTag};
22use crate::basic::serv::iam_account_serv::IamAccountServ;
23use crate::basic::serv::iam_app_serv::IamAppServ;
24use crate::basic::serv::iam_cert_serv::IamCertServ;
25use crate::basic::serv::iam_rel_serv::IamRelServ;
26use crate::iam_config::IamConfig;
27use crate::iam_constants::{self, IAM_AVATAR};
28use crate::iam_enumeration::{IamCertTokenKind, IamRelKind};
29pub struct IamIdentCacheServ;
30
31impl IamIdentCacheServ {
32 pub async fn add_token(
33 token: &str,
34 token_kind: &IamCertTokenKind,
35 rel_iam_item_id: &str,
36 renewal_expire_sec: Option<i64>,
37 expire_sec: i64,
38 coexist_num: i16,
39 funs: &TardisFunsInst,
40 ) -> TardisResult<()> {
41 let token_value = if let Some(renewal_expire_sec) = renewal_expire_sec {
42 format!("{token_kind},{rel_iam_item_id},{}", renewal_expire_sec)
43 } else {
44 format!("{token_kind},{rel_iam_item_id}")
45 };
46 log::trace!("add token: token={}", token);
47 if expire_sec > 0 {
48 funs.cache()
49 .set_ex(
50 format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str(),
51 token_value.as_str(),
52 expire_sec as u64,
53 )
54 .await?;
55 } else {
56 funs.cache().set(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str(), token_value.as_str()).await?;
57 }
58 funs.cache()
59 .hset(
60 format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, rel_iam_item_id).as_str(),
61 token,
62 &format!("{},{}", token_kind, Utc::now().timestamp_nanos_opt().expect("maybe in 23rd centery")),
63 )
64 .await?;
65 if coexist_num != 0 {
67 let old_tokens = funs.cache().hgetall(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, rel_iam_item_id).as_str()).await?;
68 let old_tokens = old_tokens
69 .into_iter()
70 .map(|(k, v)| {
71 (
72 k,
73 v.split(',').next().unwrap_or("").to_string(),
74 i64::from_str(v.split(',').nth(1).unwrap_or("")).unwrap_or(0),
75 )
76 })
77 .filter(|(_, kind, _)| kind == token_kind.to_string().as_str())
78 .sorted_by(|(_, _, t1), (_, _, t2)| t2.cmp(t1))
79 .skip(coexist_num as usize)
80 .map(|(token, _, _)| token)
81 .collect::<Vec<String>>();
82 for old_token in old_tokens {
83 Self::delete_token_by_token(&old_token, None, funs).await?;
84 }
85 }
86 Ok(())
87 }
88
89 pub async fn delete_token_by_token(token: &str, ip: Option<String>, funs: &TardisFunsInst) -> TardisResult<()> {
90 log::trace!("delete token: token={}", token);
91 if let Some(token_info) = funs.cache().get(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str()).await? {
92 let iam_item_id = token_info.split(',').nth(1).unwrap_or("");
93 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str()).await?;
94 Self::delete_double_auth(iam_item_id, funs).await?;
95 funs.cache().hdel(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, iam_item_id).as_str(), token).await?;
96
97 let mut mock_ctx = TardisContext::default();
98 if let Ok(account_context) = Self::get_account_context(iam_item_id, "", funs).await {
99 mock_ctx = account_context;
100 } else {
101 mock_ctx.owner = iam_item_id.to_string();
102 let own_paths = RbumItemServ::get_rbum(
103 iam_item_id,
104 &RbumBasicFilterReq {
105 ignore_scope: true,
106 with_sub_own_paths: true,
107 own_paths: Some("".to_string()),
108 ..Default::default()
109 },
110 funs,
111 &mock_ctx,
112 )
113 .await?
114 .own_paths;
115 mock_ctx.own_paths = own_paths;
116 }
117 add_ip(ip, &mock_ctx).await?;
118
119 let _ = IamLogClient::add_ctx_task(
120 LogParamTag::IamAccount,
121 Some(iam_item_id.to_string()),
122 "下线账号".to_string(),
123 Some("OfflineAccount".to_string()),
124 &mock_ctx,
125 )
126 .await;
127 let _ = IamLogClient::add_ctx_task(
128 LogParamTag::SecurityVisit,
129 Some(iam_item_id.to_string()),
130 "退出".to_string(),
131 Some("Quit".to_string()),
132 &mock_ctx,
133 )
134 .await;
135
136 mock_ctx.execute_task().await?;
137 }
138 Ok(())
139 }
140
141 pub async fn delete_tokens_and_contexts_by_tenant_or_app(tenant_or_app_id: &str, is_app: bool, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
142 let tenant_or_app_id = tenant_or_app_id.to_string();
143 let own_paths = if is_app {
144 IamAppServ::peek_item(
145 &tenant_or_app_id,
146 &IamAppFilterReq {
147 basic: RbumBasicFilterReq {
148 with_sub_own_paths: true,
149 ..Default::default()
150 },
151 ..Default::default()
152 },
153 funs,
154 ctx,
155 )
156 .await?
157 .own_paths
158 } else {
159 tenant_or_app_id.clone()
160 };
161 let ctx_clone = ctx.clone();
162 TaskProcessor::execute_task_with_ctx(
163 &funs.conf::<IamConfig>().cache_key_async_task_status,
164 move |_task_id| async move {
165 let funs = iam_constants::get_tardis_inst();
166 let filter = IamAccountFilterReq {
167 basic: RbumBasicFilterReq {
168 own_paths: Some(own_paths),
169 with_sub_own_paths: true,
170 ..Default::default()
171 },
172 ..Default::default()
173 };
174 let mut count = IamAccountServ::count_items(&filter, &funs, &ctx_clone).await.unwrap_or_default() as isize;
175 let mut page_number = 1;
176 while count > 0 {
177 let mut ids = Vec::new();
178 if let Ok(page) = IamAccountServ::paginate_id_items(&filter, page_number, 100, None, None, &funs, &ctx_clone).await {
179 ids = page.records;
180 }
181 for id in ids {
182 let account_context = Self::get_account_context(&id, "", &funs).await;
183 if let Ok(account_context) = account_context {
184 if account_context.own_paths == ctx_clone.own_paths {
185 Self::delete_tokens_and_contexts_by_account_id(&id, get_real_ip_from_ctx(&ctx_clone).await?, &funs).await?;
186 }
187 }
188 }
189 page_number += 1;
190 count -= 100;
191 }
192 if is_app {
193 let mut count = IamRelServ::count_to_rels(&IamRelKind::IamAccountApp, &tenant_or_app_id, &funs, &ctx_clone).await.unwrap_or_default() as isize;
194 let mut page_number = 1;
195 while count > 0 {
196 let mut ids = Vec::new();
197 if let Ok(page) = IamRelServ::paginate_to_id_rels(&IamRelKind::IamAccountApp, &tenant_or_app_id, page_number, 100, None, None, &funs, &ctx_clone).await {
198 ids = page.records;
199 }
200 for id in ids {
201 let account_context = Self::get_account_context(&id, "", &funs).await;
202 if let Ok(account_context) = account_context {
203 if account_context.own_paths == ctx_clone.own_paths {
204 Self::delete_tokens_and_contexts_by_account_id(&id, get_real_ip_from_ctx(&ctx_clone).await?, &funs).await?;
205 }
206 }
207 }
208 page_number += 1;
209 count -= 100;
210 }
211 }
212 Ok(())
213 },
214 &funs.cache(),
215 IAM_AVATAR.to_owned(),
216 Some(vec![format!("account/{}", ctx.owner)]),
217 ctx,
218 )
219 .await?;
220 Ok(())
221 }
222
223 pub async fn delete_tokens_and_contexts_by_account_id(account_id: &str, ip: Option<String>, funs: &TardisFunsInst) -> TardisResult<()> {
224 log::trace!("delete tokens and contexts: account_id={}", account_id);
225 let tokens = funs.cache().hgetall(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, account_id).as_str()).await?;
226 for (token, _) in tokens.iter() {
227 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str()).await?;
228 }
229 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, account_id).as_str()).await?;
230 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_id).as_str()).await?;
231
232 let mock_ctx = TardisContext { ..Default::default() };
233 add_ip(ip, &mock_ctx).await?;
234 let _ = IamLogClient::add_ctx_task(
235 LogParamTag::IamAccount,
236 Some(account_id.to_string()),
237 "下线账号".to_string(),
238 Some("OfflineAccount".to_string()),
239 &mock_ctx,
240 )
241 .await;
242
243 mock_ctx.execute_task().await?;
244
245 Ok(())
246 }
247
248 pub async fn exist_token_by_account_id(account_id: &str, funs: &TardisFunsInst) -> TardisResult<bool> {
249 log::trace!("exist tokens: account_id={}", account_id);
250 let tokens = funs.cache().hgetall(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_rel_, account_id).as_str()).await?;
251 for (token, _) in tokens.iter() {
252 if funs.cache().exists(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, token).as_str()).await? {
253 return Ok(true);
254 }
255 }
256 Ok(false)
257 }
258
259 pub async fn refresh_account_info_by_account_id(account_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
260 log::trace!("refresh account info: account_id={}", account_id);
261 let tenant_info = funs.cache().hget(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_id).as_str(), "").await?;
262 if tenant_info.is_none() {
263 return Ok(());
264 }
265
266 let tenant_id = TardisFuns::json.str_to_obj::<TardisContext>(&tenant_info.unwrap())?.own_paths;
267 let mock_ctx = TardisContext {
268 own_paths: tenant_id.clone(),
269 ..Default::default()
270 };
271 let _ = IamCertServ::package_tardis_account_context_and_resp(account_id, &tenant_id, "".to_string(), None, funs, &mock_ctx).await;
272 Ok(())
273 }
274
275 pub async fn delete_lock_by_account_id(account_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
276 log::trace!("delete lock: account_id={}", account_id);
277 funs.cache().del(&format!("{}{}", funs.rbum_conf_cache_key_cert_locked_(), &account_id)).await?;
278 Ok(())
279 }
280
281 pub async fn add_contexts(account_info: &IamAccountInfoResp, tenant_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
282 log::trace!("add contexts: account_id={:?}", account_info);
283 funs.cache()
284 .hset(
285 format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_info.account_id).as_str(),
286 "",
287 &TardisFuns::json.obj_to_string(&TardisContext {
288 own_paths: tenant_id.to_string(),
289 owner: account_info.account_id.to_string(),
290 roles: account_info.roles.keys().map(|id| id.to_string()).collect(),
291 groups: account_info.groups.keys().map(|id| id.to_string()).collect(),
292 ..Default::default()
293 })?,
294 )
295 .await?;
296 for account_app_info in &account_info.apps {
297 funs.cache()
298 .hset(
299 format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_info.account_id).as_str(),
300 &account_app_info.app_id,
301 &TardisFuns::json.obj_to_string(&TardisContext {
302 own_paths: format!("{}/{}", tenant_id, account_app_info.app_id).to_string(),
303 owner: account_info.account_id.to_string(),
304 roles: account_app_info.roles.keys().map(|id| id.to_string()).collect(),
305 groups: account_app_info.groups.keys().map(|id| id.to_string()).collect(),
306 ..Default::default()
307 })?,
308 )
309 .await?;
310 }
311 Ok(())
312 }
313
314 pub async fn get_account_context(account_id: &str, field: &str, funs: &TardisFunsInst) -> TardisResult<TardisContext> {
315 let mut context = if let Some(context) = funs.cache().hget(format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_id).as_str(), field).await? {
316 TardisFuns::json.str_to_obj::<TardisContext>(&context)?
317 } else {
318 return Err(funs.err().not_found("get_account_context", "get", "not found context", "404-iam-cache-context-not-exist"));
319 };
320 if !field.is_empty() {
321 if let Some(tenant_context) = funs.cache().hget(&format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_id), "").await? {
322 let tenant_context = TardisFuns::json.str_to_obj::<TardisContext>(&tenant_context)?;
323 if !tenant_context.roles.is_empty() {
324 context.roles.extend(tenant_context.roles);
325 }
326 if !tenant_context.groups.is_empty() {
327 context.groups.extend(tenant_context.groups);
328 }
329 }
330 }
331 Ok(context)
332 }
333
334 pub async fn get_context(fetch_req: &IamContextFetchReq, funs: &TardisFunsInst) -> TardisResult<TardisContext> {
335 if let Some(token_info) = funs.cache().get(format!("{}{}", funs.conf::<IamConfig>().cache_key_token_info_, &fetch_req.token).as_str()).await? {
336 let account_id = token_info.split(',').nth(1).unwrap_or("");
337 if let Some(context) = funs
338 .cache()
339 .hget(
340 format!("{}{}", funs.conf::<IamConfig>().cache_key_account_info_, account_id).as_str(),
341 fetch_req.app_id.as_ref().unwrap_or(&"".to_string()),
342 )
343 .await?
344 {
345 return TardisFuns::json.str_to_obj(&context);
346 }
347 }
348 Err(funs.err().not_found("iam_cache_context", "get", "not found context", "404-iam-cache-context-not-exist"))
349 }
350
351 pub async fn add_aksk(ak: &str, sk: &str, tenant_id: &str, app_id: Option<String>, expire_sec: i64, funs: &TardisFunsInst) -> TardisResult<()> {
352 log::trace!("add aksk: ak={},sk={}", ak, sk);
353 if expire_sec > 0 {
354 funs.cache()
355 .set_ex(
356 format!("{}{}", funs.conf::<IamConfig>().cache_key_aksk_info_, ak).as_str(),
357 format!("{},{},{}", sk, tenant_id, app_id.unwrap_or_default()).as_str(),
358 expire_sec as u64,
359 )
360 .await?;
361 } else {
362 funs.cache()
363 .set(
364 format!("{}{}", funs.conf::<IamConfig>().cache_key_aksk_info_, ak).as_str(),
365 format!("{},{},{}", sk, tenant_id, app_id.unwrap_or_default()).as_str(),
366 )
367 .await?;
368 }
369 Ok(())
370 }
371
372 pub async fn add_or_modify_gateway_rule_info(ak: &str, rule_name: &str, match_method: Option<&str>, value: &str, funs: &TardisFunsInst) -> TardisResult<()> {
373 log::trace!(
374 "add gateway_rule_info: ak={},rule_name={},match_method={},value={}",
375 ak,
376 rule_name,
377 match_method.unwrap_or("*"),
378 value
379 );
380 let match_path = &funs.conf::<IamConfig>().gateway_openapi_path;
381 funs.cache()
382 .set(
383 format!(
384 "{}{}:{}:{}:{}",
385 funs.conf::<IamConfig>().cache_key_gateway_rule_info_,
386 rule_name,
387 match_method.unwrap_or("*"),
388 match_path,
389 ak
390 )
391 .as_str(),
392 value,
393 )
394 .await?;
395 Ok(())
396 }
397
398 pub async fn get_gateway_cumulative_count(ak: &str, match_method: Option<&str>, funs: &TardisFunsInst) -> TardisResult<Option<String>> {
399 let match_path = &funs.conf::<IamConfig>().gateway_openapi_path;
400 let result = funs
401 .cache()
402 .get(&format!(
403 "{}{}:{}:{}:{}:cumulative-count",
404 funs.conf::<IamConfig>().cache_key_gateway_rule_info_,
405 iam_constants::OPENAPI_GATEWAY_PLUGIN_COUNT,
406 match_method.unwrap_or("*"),
407 match_path,
408 ak
409 ))
410 .await?;
411 Ok(result)
412 }
413
414 pub async fn get_gateway_rule_info(ak: &str, rule_name: &str, match_method: Option<&str>, funs: &TardisFunsInst) -> TardisResult<Option<String>> {
415 let match_path = &funs.conf::<IamConfig>().gateway_openapi_path;
416 let result = funs
417 .cache()
418 .get(
419 format!(
420 "{}{}:{}:{}:{}",
421 funs.conf::<IamConfig>().cache_key_gateway_rule_info_,
422 rule_name,
423 match_method.unwrap_or("*"),
424 match_path,
425 ak
426 )
427 .as_str(),
428 )
429 .await?;
430 Ok(result)
431 }
432
433 pub async fn delete_aksk(ak: &str, funs: &TardisFunsInst) -> TardisResult<()> {
434 log::trace!("delete aksk: ak={}", ak);
435
436 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_aksk_info_, ak).as_str()).await?;
437
438 Ok(())
439 }
440
441 pub async fn add_double_auth(account_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
442 log::trace!("add double auth: account_id={}", account_id);
443
444 funs.cache()
445 .set_ex(
446 format!("{}{}", funs.conf::<IamConfig>().cache_key_double_auth_info, account_id).as_str(),
447 "",
448 funs.conf::<IamConfig>().cache_key_double_auth_expire_sec as u64,
449 )
450 .await?;
451
452 Ok(())
453 }
454 pub async fn delete_double_auth(account_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
455 log::trace!("delete double auth: account_id={}", account_id);
456 if (funs.cache().get(format!("{}{}", funs.conf::<IamConfig>().cache_key_double_auth_info, account_id).as_str()).await?).is_some() {
457 funs.cache().del(format!("{}{}", funs.conf::<IamConfig>().cache_key_double_auth_info, account_id).as_str()).await?;
458 }
459 Ok(())
460 }
461}
462
463pub struct IamResCacheServ;
464
465impl IamResCacheServ {
466 pub async fn add_res(item_code: &str, action: &str, crypto_req: bool, crypto_resp: bool, double_auth: bool, need_login: bool, funs: &TardisFunsInst) -> TardisResult<()> {
467 let uri_mixed = Self::package_uri_mixed(item_code, action);
468 log::trace!("add res: uri_mixed={}", uri_mixed);
469 let add_res_dto = IamCacheResRelAddOrModifyDto {
470 need_crypto_req: crypto_req,
471 need_crypto_resp: crypto_resp,
472 need_double_auth: double_auth,
473 need_login,
474 ..Default::default()
475 };
476 funs.cache().hset(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed, &TardisFuns::json.obj_to_string(&add_res_dto)?).await?;
477 Self::add_change_trigger(&uri_mixed, funs).await
478 }
479
480 pub async fn delete_res(item_code: &str, action: &str, funs: &TardisFunsInst) -> TardisResult<()> {
481 let uri_mixed = Self::package_uri_mixed(item_code, action);
482 log::trace!("delete res: uri_mixed={}", uri_mixed);
483 funs.cache().hdel(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed).await?;
484 Self::add_change_trigger(&uri_mixed, funs).await
485 }
486
487 pub async fn add_anonymous_res_rel(item_code: &str, action: &str, st: Option<i64>, et: Option<i64>, funs: &TardisFunsInst) -> TardisResult<()> {
489 let res_auth = IamCacheResAuth {
490 tenants: "#*#".to_string(),
491 st,
492 et,
493 ..Default::default()
494 };
495 let mut res_dto = IamCacheResRelAddOrModifyDto {
496 auth: Some(res_auth),
497 need_crypto_req: false,
498 need_crypto_resp: false,
499 need_double_auth: false,
500 need_login: false,
501 };
502 let uri_mixed = Self::package_uri_mixed(item_code, action);
503 let rels = funs.cache().hget(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed).await?;
504 if let Some(rels) = rels {
505 let old_res_dto = TardisFuns::json.str_to_obj::<IamCacheResRelAddOrModifyDto>(&rels)?;
506 res_dto.need_crypto_req = old_res_dto.need_crypto_req;
507 res_dto.need_crypto_resp = old_res_dto.need_crypto_resp;
508 res_dto.need_double_auth = old_res_dto.need_double_auth;
509 res_dto.need_login = old_res_dto.need_login;
510 }
511 funs.cache().hset(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed, &TardisFuns::json.obj_to_string(&res_dto)?).await?;
512 Self::add_change_trigger(&uri_mixed, funs).await
513 }
514
515 pub async fn add_or_modify_res_rel(item_code: &str, action: &str, add_or_modify_req: &IamCacheResRelAddOrModifyReq, funs: &TardisFunsInst) -> TardisResult<()> {
516 if add_or_modify_req.st.is_some() || add_or_modify_req.et.is_some() {
517 return Err(funs.err().conflict("iam_cache_res", "add_or_modify", "st and et must be none", "409-iam-cache-res-date-not-none"));
519 }
520 let mut res_auth = IamCacheResAuth {
521 accounts: format!("#{}#", add_or_modify_req.accounts.join("#")),
522 roles: format!("#{}#", add_or_modify_req.roles.join("#")),
523 groups: format!("#{}#", add_or_modify_req.groups.join("#")),
524 apps: format!("#{}#", add_or_modify_req.apps.join("#")),
525 tenants: format!("#{}#", add_or_modify_req.tenants.join("#")),
526 aks: format!("#{}#", add_or_modify_req.aks.join("#")),
527 ..Default::default()
528 };
529 let mut res_dto = IamCacheResRelAddOrModifyDto {
530 auth: None,
531 need_crypto_req: false,
532 need_crypto_resp: false,
533 need_double_auth: false,
534 need_login: false,
535 };
536 let uri_mixed = Self::package_uri_mixed(item_code, action);
537 log::trace!("add or modify res rel: uri_mixed={}", uri_mixed);
538 let rels = funs.cache().hget(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed).await?;
539 if let Some(rels) = rels {
540 let old_res_dto = TardisFuns::json.str_to_obj::<IamCacheResRelAddOrModifyDto>(&rels)?;
541 if let Some(old_auth) = old_res_dto.auth {
542 res_auth.accounts = format!("{}{}", res_auth.accounts, old_auth.accounts);
543 res_auth.roles = format!("{}{}", res_auth.roles, old_auth.roles);
544 res_auth.groups = format!("{}{}", res_auth.groups, old_auth.groups);
545 res_auth.apps = format!("{}{}", res_auth.apps, old_auth.apps);
546 res_auth.tenants = format!("{}{}", res_auth.tenants, old_auth.tenants);
547 res_auth.aks = format!("{}{}", res_auth.aks, old_auth.aks);
548 }
549
550 if let Some(need_crypto_req) = add_or_modify_req.need_crypto_req {
551 res_dto.need_crypto_req = need_crypto_req
552 } else {
553 res_dto.need_crypto_req = old_res_dto.need_crypto_req
554 }
555 if let Some(need_crypto_resp) = add_or_modify_req.need_crypto_resp {
556 res_dto.need_crypto_resp = need_crypto_resp
557 } else {
558 res_dto.need_crypto_resp = old_res_dto.need_crypto_resp
559 }
560 if let Some(need_double_auth) = add_or_modify_req.need_double_auth {
561 res_dto.need_double_auth = need_double_auth
562 } else {
563 res_dto.need_double_auth = old_res_dto.need_double_auth
564 }
565 if let Some(need_login) = add_or_modify_req.need_login {
566 res_dto.need_login = need_login
567 } else {
568 res_dto.need_login = old_res_dto.need_login
569 }
570 }
571 res_auth.accounts = res_auth.accounts.replace("##", "#");
572 res_auth.roles = res_auth.roles.replace("##", "#");
573 res_auth.groups = res_auth.groups.replace("##", "#");
574 res_auth.apps = res_auth.apps.replace("##", "#");
575 res_auth.tenants = res_auth.tenants.replace("##", "#");
576 res_auth.aks = res_auth.aks.replace("##", "#");
577
578 if (res_auth.accounts == "#" || res_auth.accounts == "##")
579 && (res_auth.roles == "#" || res_auth.roles == "##")
580 && (res_auth.groups == "#" || res_auth.groups == "##")
581 && (res_auth.apps == "#" || res_auth.apps == "##")
582 && (res_auth.tenants == "#" || res_auth.tenants == "##")
583 && (res_auth.aks == "#" || res_auth.aks == "##")
584 {
585 res_dto.auth = None;
586 } else {
587 res_dto.auth = Some(res_auth);
588 }
589
590 funs.cache().hset(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed, &TardisFuns::json.obj_to_string(&res_dto)?).await?;
591 Self::add_change_trigger(&uri_mixed, funs).await
592 }
593
594 pub async fn delete_res_rel(item_code: &str, action: &str, delete_req: &IamCacheResRelDeleteReq, funs: &TardisFunsInst) -> TardisResult<()> {
595 let uri_mixed = Self::package_uri_mixed(item_code, action);
596 log::trace!("delete res rel: uri_mixed={}", uri_mixed);
597 let rels = funs.cache().hget(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed).await?;
598 if let Some(rels) = rels {
599 let mut res_dto = TardisFuns::json.str_to_obj::<IamCacheResRelAddOrModifyDto>(&rels)?;
600 if let Some(res_auth) = res_dto.auth {
601 let mut auth = res_auth;
602 for account in &delete_req.accounts {
603 while auth.accounts.contains(&format!("#{account}#")) {
604 auth.accounts = auth.accounts.replacen(&format!("#{account}#"), "#", 1);
605 }
606 }
607 for role in &delete_req.roles {
608 while auth.roles.contains(&format!("#{role}#")) {
609 auth.roles = auth.roles.replacen(&format!("#{role}#"), "#", 1);
610 }
611 }
612 for group in &delete_req.groups {
613 while auth.groups.contains(&format!("#{group}#")) {
614 auth.groups = auth.groups.replacen(&format!("#{group}#"), "#", 1);
615 }
616 }
617 for app in &delete_req.apps {
618 while auth.apps.contains(&format!("#{app}#")) {
619 auth.apps = auth.apps.replacen(&format!("#{app}#"), "#", 1);
620 }
621 }
622 for tenant in &delete_req.tenants {
623 while auth.tenants.contains(&format!("#{tenant}#")) {
624 auth.tenants = auth.tenants.replacen(&format!("#{tenant}#"), "#", 1);
625 }
626 }
627 for ak in &delete_req.aks {
628 while auth.aks.contains(&format!("#{ak}#")) {
629 auth.aks = auth.aks.replacen(&format!("#{ak}#"), "#", 1);
630 }
631 }
632 if (auth.accounts == "#" || auth.accounts == "##")
633 && (auth.roles == "#" || auth.roles == "##")
634 && (auth.groups == "#" || auth.groups == "##")
635 && (auth.apps == "#" || auth.apps == "##")
636 && (auth.tenants == "#" || auth.tenants == "##")
637 {
638 res_dto.auth = None;
639 } else {
640 res_dto.auth = Some(auth);
641 }
642 }
643 funs.cache().hset(&funs.conf::<IamConfig>().cache_key_res_info, &uri_mixed, &TardisFuns::json.obj_to_string(&res_dto)?).await?;
644 return Self::add_change_trigger(&uri_mixed, funs).await;
645 }
646 Err(funs.err().not_found("iam_cache_res", "delete", "not found res rel", "404-iam-cache-res-rel-not-exist"))
647 }
648
649 async fn add_change_trigger(uri: &str, funs: &TardisFunsInst) -> TardisResult<()> {
650 funs.cache()
651 .set_ex(
652 &format!("{}{}", funs.conf::<IamConfig>().cache_key_res_changed_info_, uri),
653 "",
654 funs.conf::<IamConfig>().cache_key_res_changed_expire_sec as u64,
655 )
656 .await?;
657 Ok(())
658 }
659
660 pub fn package_uri_mixed(item_code: &str, action: &str) -> String {
661 let domain_idx = item_code.find('/').unwrap_or(item_code.len());
662 let domain = &item_code[0..domain_idx];
663 let path_and_query = if domain_idx != item_code.len() { &item_code[domain_idx + 1..] } else { "" };
664 format!(
665 "{}://{}/{}##{}",
666 iam_constants::RBUM_KIND_CODE_IAM_RES.to_lowercase(),
667 domain.to_lowercase(),
668 path_and_query,
669 action
670 )
671 }
672}
673
674#[derive(Serialize, Deserialize, Debug, Clone, Default)]
675#[serde(default)]
676struct IamCacheResRelAddOrModifyDto {
677 pub auth: Option<IamCacheResAuth>,
678 pub need_crypto_req: bool,
679 pub need_crypto_resp: bool,
680 pub need_double_auth: bool,
681 pub need_login: bool,
682}
683#[derive(Serialize, Deserialize, Debug, Clone, Default)]
684#[serde(default)]
685struct IamCacheResAuth {
686 pub accounts: String,
687 pub roles: String,
688 pub groups: String,
689 pub apps: String,
690 pub tenants: String,
691 pub aks: String,
692 pub st: Option<i64>,
693 pub et: Option<i64>,
694}
695
696pub struct IamCacheResRelAddOrModifyReq {
697 pub st: Option<i64>,
698 pub et: Option<i64>,
699 pub accounts: Vec<String>,
700 pub roles: Vec<String>,
701 pub groups: Vec<String>,
702 pub apps: Vec<String>,
703 pub tenants: Vec<String>,
704 pub aks: Vec<String>,
705 pub need_crypto_req: Option<bool>,
706 pub need_crypto_resp: Option<bool>,
707 pub need_double_auth: Option<bool>,
708 pub need_login: Option<bool>,
709}
710
711#[derive(Deserialize, Serialize, Clone, Debug)]
712pub struct IamCacheResRelDeleteReq {
713 pub accounts: Vec<String>,
714 pub roles: Vec<String>,
715 pub groups: Vec<String>,
716 pub apps: Vec<String>,
717 pub tenants: Vec<String>,
718 pub aks: Vec<String>,
719}