bios_basic/rbum/serv/
rbum_cert_serv.rs

1use async_trait::async_trait;
2use fancy_regex::Regex;
3use tardis::basic::dto::TardisContext;
4use tardis::basic::field::TrimString;
5use tardis::basic::result::TardisResult;
6use tardis::cache::AsyncCommands;
7use tardis::chrono::{DateTime, Duration, TimeDelta, Utc};
8use tardis::db::reldb_client::IdResp;
9use tardis::db::sea_orm::sea_query::*;
10use tardis::db::sea_orm::*;
11use tardis::db::sea_orm::{self, IdenStatic};
12use tardis::TardisFunsInst;
13use tardis::{log, TardisFuns};
14
15use crate::rbum::domain::{rbum_cert, rbum_cert_conf, rbum_domain, rbum_item};
16use crate::rbum::dto::rbum_cert_conf_dto::{RbumCertConfAddReq, RbumCertConfDetailResp, RbumCertConfIdAndExtResp, RbumCertConfModifyReq, RbumCertConfSummaryResp};
17use crate::rbum::dto::rbum_cert_dto::{RbumCertAddReq, RbumCertDetailResp, RbumCertModifyReq, RbumCertSummaryResp};
18use crate::rbum::dto::rbum_filer_dto::{RbumBasicFilterReq, RbumCertConfFilterReq, RbumCertFilterReq};
19use crate::rbum::rbum_config::RbumConfigApi;
20use crate::rbum::rbum_enumeration::{RbumCertConfStatusKind, RbumCertRelKind, RbumCertStatusKind};
21use crate::rbum::serv::rbum_crud_serv::{RbumCrudOperation, RbumCrudQueryPackage};
22use crate::rbum::serv::rbum_domain_serv::RbumDomainServ;
23use crate::rbum::serv::rbum_item_serv::RbumItemServ;
24use crate::rbum::serv::rbum_rel_serv::RbumRelServ;
25use crate::rbum::serv::rbum_set_serv::RbumSetServ;
26
27pub struct RbumCertConfServ;
28
29pub struct RbumCertServ;
30
31#[async_trait]
32impl RbumCrudOperation<rbum_cert_conf::ActiveModel, RbumCertConfAddReq, RbumCertConfModifyReq, RbumCertConfSummaryResp, RbumCertConfDetailResp, RbumCertConfFilterReq>
33    for RbumCertConfServ
34{
35    fn get_table_name() -> &'static str {
36        rbum_cert_conf::Entity.table_name()
37    }
38
39    async fn before_add_rbum(add_req: &mut RbumCertConfAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
40        if let Some(is_basic) = add_req.is_basic {
41            // If is_basic is true, sk_dynamic must be false
42            if is_basic {
43                add_req.sk_dynamic = Some(false);
44                if funs
45                    .db()
46                    .count(
47                        Query::select()
48                            .column(rbum_cert_conf::Column::Id)
49                            .from(rbum_cert_conf::Entity)
50                            .and_where(Expr::col(rbum_cert_conf::Column::IsBasic).eq(true))
51                            .and_where(Expr::col(rbum_cert_conf::Column::RelRbumDomainId).eq(add_req.rel_rbum_domain_id.as_str()))
52                            .and_where(Expr::col(rbum_cert_conf::Column::RelRbumItemId).eq(add_req.rel_rbum_item_id.as_ref().unwrap_or(&"".to_string()).as_str())),
53                    )
54                    .await?
55                    > 0
56                {
57                    return Err(funs.err().conflict(&Self::get_obj_name(), "add", "is_basic already exists", "409-rbum-cert-conf-basic-exist"));
58                }
59            }
60        }
61        Self::check_scope(&add_req.rel_rbum_domain_id, RbumDomainServ::get_table_name(), funs, ctx).await?;
62        if let Some(rel_rbum_item_id) = &add_req.rel_rbum_item_id {
63            Self::check_scope(rel_rbum_item_id, RbumItemServ::get_table_name(), funs, ctx).await?;
64        }
65        if let Some(ak_rule) = &add_req.ak_rule {
66            Regex::new(ak_rule).map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "add", &format!("ak rule is invalid:{e}"), "400-rbum-cert-conf-ak-rule-invalid"))?;
67        }
68        if let Some(sk_rule) = &add_req.sk_rule {
69            Regex::new(sk_rule).map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "add", &format!("sk rule is invalid:{e}"), "400-rbum-cert-conf-sk-rule-invalid"))?;
70        }
71        if funs
72            .db()
73            .count(
74                Query::select()
75                    .column(rbum_cert_conf::Column::Id)
76                    .from(rbum_cert_conf::Entity)
77                    .and_where(Expr::col(rbum_cert_conf::Column::Kind).eq(add_req.kind.to_string()))
78                    .and_where(Expr::col(rbum_cert_conf::Column::Supplier).eq(add_req.supplier.as_ref().unwrap_or(&TrimString::from("")).to_string()))
79                    .and_where(Expr::col(rbum_cert_conf::Column::RelRbumDomainId).eq(add_req.rel_rbum_domain_id.as_str()))
80                    .and_where(Expr::col(rbum_cert_conf::Column::RelRbumItemId).eq(add_req.rel_rbum_item_id.as_ref().unwrap_or(&"".to_string()).as_str())),
81            )
82            .await?
83            > 0
84        {
85            return Err(funs.err().conflict(&Self::get_obj_name(), "add", &format!("code {} already exists", add_req.kind), "409-rbum-*-code-exist"));
86        }
87        Ok(())
88    }
89
90    async fn package_add(add_req: &RbumCertConfAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_cert_conf::ActiveModel> {
91        Ok(rbum_cert_conf::ActiveModel {
92            id: Set(TardisFuns::field.nanoid()),
93            kind: Set(add_req.kind.to_string()),
94            supplier: Set(add_req.supplier.as_ref().unwrap_or(&TrimString("".to_string())).to_string()),
95            name: Set(add_req.name.to_string()),
96            note: Set(add_req.note.as_ref().unwrap_or(&"".to_string()).to_string()),
97            ak_note: Set(add_req.ak_note.as_ref().unwrap_or(&"".to_string()).to_string()),
98            ak_rule: Set(add_req.ak_rule.as_ref().unwrap_or(&"".to_string()).to_string()),
99            sk_note: Set(add_req.sk_note.as_ref().unwrap_or(&"".to_string()).to_string()),
100            sk_rule: Set(add_req.sk_rule.as_ref().unwrap_or(&"".to_string()).to_string()),
101            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
102            sk_need: Set(add_req.sk_need.unwrap_or(true)),
103            sk_dynamic: Set(add_req.sk_dynamic.unwrap_or(false)),
104            sk_encrypted: Set(add_req.sk_encrypted.unwrap_or(false)),
105            repeatable: Set(add_req.repeatable.unwrap_or(true)),
106            is_basic: Set(add_req.is_basic.unwrap_or(false)),
107            rest_by_kinds: Set(add_req.rest_by_kinds.as_ref().unwrap_or(&"".to_string()).to_string()),
108            expire_sec: Set(add_req.expire_sec.unwrap_or(3600 * 24 * 365)),
109            sk_lock_cycle_sec: Set(add_req.sk_lock_cycle_sec.unwrap_or(0)),
110            sk_lock_err_times: Set(add_req.sk_lock_err_times.unwrap_or(0)),
111            sk_lock_duration_sec: Set(add_req.sk_lock_duration_sec.unwrap_or(0)),
112            coexist_num: Set(add_req.coexist_num.unwrap_or(1)),
113            conn_uri: Set(add_req.conn_uri.as_ref().unwrap_or(&"".to_string()).to_string()),
114            status: Set(add_req.status.to_int()),
115            rel_rbum_domain_id: Set(add_req.rel_rbum_domain_id.to_string()),
116            rel_rbum_item_id: Set(add_req.rel_rbum_item_id.as_ref().unwrap_or(&"".to_string()).to_string()),
117            ..Default::default()
118        })
119    }
120
121    async fn before_modify_rbum(id: &str, modify_req: &mut RbumCertConfModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
122        Self::check_ownership(id, funs, ctx).await?;
123        if let Some(true) = modify_req.is_basic {
124            let rbum_cert_conf_resp = Self::peek_rbum(id, &RbumCertConfFilterReq::default(), funs, ctx).await?;
125            if funs
126                .db()
127                .count(
128                    Query::select()
129                        .column(rbum_cert_conf::Column::Id)
130                        .from(rbum_cert_conf::Entity)
131                        .and_where(Expr::col(rbum_cert_conf::Column::IsBasic).eq(true))
132                        .and_where(Expr::col(rbum_cert_conf::Column::Id).ne(id))
133                        .and_where(Expr::col(rbum_cert_conf::Column::RelRbumDomainId).eq(rbum_cert_conf_resp.rel_rbum_domain_id.as_str()))
134                        .and_where(Expr::col(rbum_cert_conf::Column::RelRbumItemId).eq(rbum_cert_conf_resp.rel_rbum_item_id.as_str())),
135                )
136                .await?
137                > 0
138            {
139                return Err(funs.err().conflict(&Self::get_obj_name(), "modify", "is_basic already exists", "409-rbum-cert-conf-basic-exist"));
140            }
141        }
142        Ok(())
143    }
144
145    async fn package_modify(id: &str, modify_req: &RbumCertConfModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_cert_conf::ActiveModel> {
146        let mut rbum_cert_conf = rbum_cert_conf::ActiveModel {
147            id: Set(id.to_string()),
148            ..Default::default()
149        };
150        if let Some(name) = &modify_req.name {
151            rbum_cert_conf.name = Set(name.to_string());
152        }
153        if let Some(note) = &modify_req.note {
154            rbum_cert_conf.note = Set(note.to_string());
155        }
156        if let Some(ak_note) = &modify_req.ak_note {
157            rbum_cert_conf.ak_note = Set(ak_note.to_string());
158        }
159        if let Some(ak_rule) = &modify_req.ak_rule {
160            rbum_cert_conf.ak_rule = Set(ak_rule.to_string());
161        }
162        if let Some(sk_note) = &modify_req.sk_note {
163            rbum_cert_conf.sk_note = Set(sk_note.to_string());
164        }
165        if let Some(sk_rule) = &modify_req.sk_rule {
166            rbum_cert_conf.sk_rule = Set(sk_rule.to_string());
167        }
168        if let Some(ext) = &modify_req.ext {
169            rbum_cert_conf.ext = Set(ext.to_string());
170        }
171        if let Some(sk_need) = modify_req.sk_need {
172            rbum_cert_conf.sk_need = Set(sk_need);
173        }
174        if let Some(sk_encrypted) = modify_req.sk_encrypted {
175            rbum_cert_conf.sk_encrypted = Set(sk_encrypted);
176        }
177        if let Some(repeatable) = modify_req.repeatable {
178            rbum_cert_conf.repeatable = Set(repeatable);
179        }
180        if let Some(is_basic) = modify_req.is_basic {
181            rbum_cert_conf.is_basic = Set(is_basic);
182            if is_basic {
183                // If is_basic is true, sk_dynamic must be false
184                rbum_cert_conf.sk_dynamic = Set(false);
185            }
186        }
187        if let Some(rest_by_kinds) = &modify_req.rest_by_kinds {
188            rbum_cert_conf.rest_by_kinds = Set(rest_by_kinds.to_string());
189        }
190        if let Some(expire_sec) = modify_req.expire_sec {
191            rbum_cert_conf.expire_sec = Set(expire_sec);
192        }
193        if let Some(sk_lock_cycle_sec) = modify_req.sk_lock_cycle_sec {
194            rbum_cert_conf.sk_lock_cycle_sec = Set(sk_lock_cycle_sec);
195        }
196        if let Some(sk_lock_err_times) = modify_req.sk_lock_err_times {
197            rbum_cert_conf.sk_lock_err_times = Set(sk_lock_err_times);
198        }
199        if let Some(sk_lock_duration_sec) = modify_req.sk_lock_duration_sec {
200            rbum_cert_conf.sk_lock_duration_sec = Set(sk_lock_duration_sec);
201        }
202        if let Some(coexist_num) = modify_req.coexist_num {
203            rbum_cert_conf.coexist_num = Set(coexist_num);
204        }
205        if let Some(conn_uri) = &modify_req.conn_uri {
206            rbum_cert_conf.conn_uri = Set(conn_uri.to_string());
207        }
208        if let Some(status) = &modify_req.status {
209            rbum_cert_conf.status = Set(status.to_int());
210        }
211        Ok(rbum_cert_conf)
212    }
213
214    async fn before_delete_rbum(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<Option<RbumCertConfDetailResp>> {
215        if funs
216            .db()
217            .count(
218                Query::select()
219                    .column(rbum_cert_conf::Column::Id)
220                    .from(rbum_cert_conf::Entity)
221                    .and_where(Expr::col(rbum_cert_conf::Column::Id).eq(id))
222                    .and_where(Expr::col(rbum_cert_conf::Column::IsBasic).eq(true)),
223            )
224            .await?
225            > 0
226        {
227            return Err(funs.err().conflict(&Self::get_obj_name(), "delete", "is_basic is true", "409-rbum-cert-conf-basic-delete"));
228        }
229        Self::check_ownership(id, funs, ctx).await?;
230        Self::check_exist_before_delete(id, RbumCertServ::get_table_name(), rbum_cert::Column::RelRbumCertConfId.as_str(), funs).await?;
231        Ok(None)
232    }
233
234    async fn package_query(is_detail: bool, filter: &RbumCertConfFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
235        let mut query = Query::select();
236        query
237            .columns(vec![
238                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Id),
239                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Kind),
240                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Supplier),
241                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Name),
242                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Note),
243                (rbum_cert_conf::Entity, rbum_cert_conf::Column::AkNote),
244                (rbum_cert_conf::Entity, rbum_cert_conf::Column::AkRule),
245                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkNote),
246                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkRule),
247                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Ext),
248                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkNeed),
249                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkDynamic),
250                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkEncrypted),
251                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Repeatable),
252                (rbum_cert_conf::Entity, rbum_cert_conf::Column::IsBasic),
253                (rbum_cert_conf::Entity, rbum_cert_conf::Column::RestByKinds),
254                (rbum_cert_conf::Entity, rbum_cert_conf::Column::ExpireSec),
255                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkLockCycleSec),
256                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkLockErrTimes),
257                (rbum_cert_conf::Entity, rbum_cert_conf::Column::SkLockDurationSec),
258                (rbum_cert_conf::Entity, rbum_cert_conf::Column::CoexistNum),
259                (rbum_cert_conf::Entity, rbum_cert_conf::Column::ConnUri),
260                (rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumDomainId),
261                (rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumItemId),
262                (rbum_cert_conf::Entity, rbum_cert_conf::Column::OwnPaths),
263                (rbum_cert_conf::Entity, rbum_cert_conf::Column::Owner),
264                (rbum_cert_conf::Entity, rbum_cert_conf::Column::CreateTime),
265                (rbum_cert_conf::Entity, rbum_cert_conf::Column::UpdateTime),
266            ])
267            .from(rbum_cert_conf::Entity);
268        if let Some(kind) = &filter.kind {
269            query.and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::Kind)).eq(kind.to_string()));
270        }
271        if let Some(supplier) = &filter.supplier {
272            query.and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert::Column::Supplier)).eq(supplier.to_string()));
273        }
274        if let Some(status) = &filter.status {
275            query.and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert::Column::Status)).eq(status.to_int()));
276        }
277        if let Some(rel_rbum_domain_id) = &filter.rel_rbum_domain_id {
278            query.and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumDomainId)).eq(rel_rbum_domain_id.to_string()));
279        }
280        if let Some(rbum_item_id) = &filter.rel_rbum_item_id {
281            query.and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumItemId)).eq(rbum_item_id.to_string()));
282        }
283        if is_detail {
284            query
285                .expr_as(Expr::col((rbum_domain::Entity, rbum_domain::Column::Name)), Alias::new("rel_rbum_domain_name"))
286                .expr_as(Expr::col((rbum_item::Entity, rbum_item::Column::Name)).if_null(""), Alias::new("rel_rbum_item_name"))
287                .inner_join(
288                    rbum_domain::Entity,
289                    Expr::col((rbum_domain::Entity, rbum_domain::Column::Id)).equals((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumDomainId)),
290                )
291                .left_join(
292                    rbum_item::Entity,
293                    Expr::col((rbum_item::Entity, rbum_item::Column::Id)).equals((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumItemId)),
294                );
295        }
296        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, false, ctx);
297        Ok(query)
298    }
299}
300
301impl RbumCertConfServ {
302    pub async fn get_rbum_cert_conf_id_and_ext_by_kind_supplier(
303        kind: &str,
304        supplier: &str,
305        ignore_status: bool,
306        rbum_domain_id: &str,
307        rbum_item_id: &str,
308        funs: &TardisFunsInst,
309    ) -> TardisResult<Option<RbumCertConfIdAndExtResp>> {
310        let mut conf_info_stat = Query::select();
311        conf_info_stat
312            .column(rbum_cert_conf::Column::Id)
313            .column(rbum_cert_conf::Column::Ext)
314            .from(rbum_cert_conf::Entity)
315            .and_where(Expr::col(rbum_cert_conf::Column::Kind).eq(kind))
316            .and_where(Expr::col(rbum_cert_conf::Column::RelRbumDomainId).eq(rbum_domain_id))
317            .and_where(Expr::col(rbum_cert_conf::Column::RelRbumItemId).eq(rbum_item_id));
318        if !ignore_status {
319            conf_info_stat.and_where(Expr::col(rbum_cert_conf::Column::Status).eq(RbumCertConfStatusKind::Enabled.to_int()));
320        }
321        if !supplier.is_empty() {
322            conf_info_stat.and_where(Expr::col(rbum_cert_conf::Column::Supplier).eq(supplier));
323        }
324        if let Some(rbum_cert_conf_id_and_ext) = funs.db().get_dto::<RbumCertConfIdAndExtResp>(&conf_info_stat).await? {
325            Ok(Some(rbum_cert_conf_id_and_ext))
326        } else {
327            Ok(None)
328        }
329    }
330}
331
332#[async_trait]
333impl RbumCrudOperation<rbum_cert::ActiveModel, RbumCertAddReq, RbumCertModifyReq, RbumCertSummaryResp, RbumCertDetailResp, RbumCertFilterReq> for RbumCertServ {
334    fn get_table_name() -> &'static str {
335        rbum_cert::Entity.table_name()
336    }
337
338    async fn before_add_rbum(add_req: &mut RbumCertAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
339        if add_req.sk.is_some() && add_req.vcode.is_some() {
340            return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "sk and vcode can only have one", "400-rbum-cert-sk-vcode-only-one"));
341        }
342        if add_req.start_time.is_none() {
343            add_req.start_time = Some(Utc::now());
344        }
345
346        match add_req.rel_rbum_kind {
347            RbumCertRelKind::Item => {
348                if !add_req.is_outside {
349                    Self::check_scope(&add_req.rel_rbum_id, RbumItemServ::get_table_name(), funs, ctx).await?;
350                }
351            }
352            RbumCertRelKind::Set => Self::check_scope(&add_req.rel_rbum_id, RbumSetServ::get_table_name(), funs, ctx).await?,
353            RbumCertRelKind::Rel => Self::check_ownership_with_table_name(&add_req.rel_rbum_id, RbumRelServ::get_table_name(), funs, ctx).await?,
354        }
355        if let Some(rel_rbum_cert_conf_id) = &add_req.rel_rbum_cert_conf_id {
356            Self::check_ownership_with_table_name(rel_rbum_cert_conf_id, RbumCertConfServ::get_table_name(), funs, ctx).await?;
357
358            let rbum_cert_conf = RbumCertConfServ::peek_rbum(
359                rel_rbum_cert_conf_id,
360                &RbumCertConfFilterReq {
361                    basic: RbumBasicFilterReq {
362                        with_sub_own_paths: true,
363                        ..Default::default()
364                    },
365                    ..Default::default()
366                },
367                funs,
368                ctx,
369            )
370            .await?;
371
372            if rbum_cert_conf.sk_need && add_req.sk.is_none() {
373                return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "sk is required", "400-rbum-cert-sk-require"));
374            }
375            if rbum_cert_conf.sk_dynamic && add_req.sk.is_some() {
376                return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "sk should be empty when dynamic model", "400-rbum-cert-sk-need-empty"));
377            }
378            if rbum_cert_conf.sk_dynamic && add_req.vcode.is_none() {
379                return Err(funs.err().bad_request(&Self::get_obj_name(), "add", "vcode is required when dynamic model", "400-rbum-cert-vcode-require"));
380            }
381
382            if !rbum_cert_conf.ak_rule.is_empty()
383                && !Regex::new(&rbum_cert_conf.ak_rule)
384                    .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "add", &format!("ak rule is invalid:{e}"), "400-rbum-cert-conf-ak-rule-invalid"))?
385                    .is_match(add_req.ak.as_ref())
386                    .unwrap_or(false)
387            {
388                return Err(funs.err().bad_request(
389                    &Self::get_obj_name(),
390                    "add",
391                    &format!("ak {} is not match ak rule", add_req.ak),
392                    "400-rbum-cert-conf-ak-rule-not-match",
393                ));
394            }
395            if rbum_cert_conf.sk_need && !rbum_cert_conf.sk_rule.is_empty() {
396                let sk = add_req.sk.as_ref().ok_or_else(|| funs.err().bad_request(&Self::get_obj_name(), "add", "sk is required", "400-rbum-cert-sk-require"))?.to_string();
397                if !Regex::new(&rbum_cert_conf.sk_rule)
398                    .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "add", &format!("sk rule is invalid:{e}"), "400-rbum-cert-conf-sk-rule-invalid"))?
399                    .is_match(&sk)
400                    .unwrap_or(false)
401                    && !add_req.ignore_check_sk
402                {
403                    return Err(funs.err().bad_request(
404                        &Self::get_obj_name(),
405                        "add",
406                        &format!("sk {} is not match sk rule", &sk),
407                        "400-rbum-cert-conf-sk-rule-not-match",
408                    ));
409                }
410            }
411
412            if funs
413                .db()
414                .count(
415                    Query::select()
416                        .column(rbum_cert::Column::Id)
417                        .from(rbum_cert::Entity)
418                        .and_where(Expr::col(rbum_cert::Column::RelRbumKind).eq(add_req.rel_rbum_kind.to_int()))
419                        .and_where(Expr::col(rbum_cert::Column::Ak).eq(add_req.ak.to_string()))
420                        .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).eq(add_req.rel_rbum_cert_conf_id.clone()))
421                        .and_where(Expr::col(rbum_cert::Column::OwnPaths).like(format!("{}%", ctx.own_paths).as_str())),
422                )
423                .await?
424                > 0
425            {
426                return Err(funs.err().conflict(&Self::get_obj_name(), "add", "ak is used", "409-rbum-cert-ak-duplicate"));
427            }
428
429            // Encrypt Sk
430            if rbum_cert_conf.sk_encrypted {
431                if let Some(sk) = &add_req.sk {
432                    let sk = Self::encrypt_sk(sk, &add_req.ak, rel_rbum_cert_conf_id)?;
433                    add_req.sk = Some(TrimString(sk));
434                }
435            }
436            // Fill Time
437            if add_req.end_time.is_none() {
438                add_req.end_time = Some(add_req.start_time.expect("ignore") + Duration::try_seconds(rbum_cert_conf.expire_sec).unwrap_or(TimeDelta::max_value()));
439            }
440            // Dynamic Sk do not require an expiration time
441            if rbum_cert_conf.sk_dynamic {
442                add_req.end_time = Some(Utc::now() + Duration::try_days(365 * 100).expect("ignore"));
443            }
444        } else {
445            // Fill Time
446            if add_req.end_time.is_none() {
447                add_req.end_time = Some(add_req.start_time.expect("ignore") + Duration::try_days(365 * 100).expect("ignore"));
448            }
449        }
450
451        Ok(())
452    }
453
454    async fn package_add(add_req: &RbumCertAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_cert::ActiveModel> {
455        Ok(rbum_cert::ActiveModel {
456            id: Set(TardisFuns::field.nanoid()),
457            ak: Set(add_req.ak.to_string()),
458            sk: Set(add_req.sk.as_ref().unwrap_or(&TrimString("".to_string())).to_string()),
459            sk_invisible: Set(add_req.sk_invisible.unwrap_or(false)),
460            kind: Set(add_req.kind.as_ref().unwrap_or(&"".to_string()).to_string()),
461            supplier: Set(add_req.supplier.as_ref().unwrap_or(&"".to_string()).to_string()),
462            ext: Set(add_req.ext.as_ref().unwrap_or(&"".to_string()).to_string()),
463            start_time: Set(add_req.start_time.expect("ignore")),
464            end_time: Set(add_req.end_time.expect("ignore")),
465            conn_uri: Set(add_req.conn_uri.as_ref().unwrap_or(&"".to_string()).to_string()),
466            status: Set(add_req.status.to_int()),
467            rel_rbum_cert_conf_id: Set(add_req.rel_rbum_cert_conf_id.as_ref().unwrap_or(&"".to_string()).to_string()),
468            rel_rbum_kind: Set(add_req.rel_rbum_kind.to_int()),
469            rel_rbum_id: Set(add_req.rel_rbum_id.to_string()),
470            ..Default::default()
471        })
472    }
473
474    async fn after_add_rbum(id: &str, add_req: &RbumCertAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
475        let rbum_cert = Self::peek_rbum(
476            id,
477            &RbumCertFilterReq {
478                basic: RbumBasicFilterReq {
479                    with_sub_own_paths: true,
480                    ..Default::default()
481                },
482                ..Default::default()
483            },
484            funs,
485            ctx,
486        )
487        .await?;
488        if let Some(rel_rbum_cert_conf_id) = &rbum_cert.rel_rbum_cert_conf_id {
489            if !rel_rbum_cert_conf_id.is_empty() {
490                let rbum_cert_conf = RbumCertConfServ::peek_rbum(
491                    rel_rbum_cert_conf_id,
492                    &RbumCertConfFilterReq {
493                        basic: RbumBasicFilterReq {
494                            with_sub_own_paths: true,
495                            ..Default::default()
496                        },
497                        ..Default::default()
498                    },
499                    funs,
500                    ctx,
501                )
502                .await?;
503                // Delete Old Certs
504                if rbum_cert_conf.coexist_num != 0 {
505                    let need_delete_rbum_cert_ids = funs
506                        .db()
507                        .find_dtos::<IdResp>(
508                            Query::select()
509                                .column(rbum_cert::Column::Id)
510                                .from(rbum_cert::Entity)
511                                .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).eq(rel_rbum_cert_conf_id))
512                                .and_where(Expr::col(rbum_cert::Column::RelRbumKind).eq(add_req.rel_rbum_kind.to_int()))
513                                .and_where(Expr::col(rbum_cert::Column::RelRbumId).eq(&add_req.rel_rbum_id))
514                                .order_by((rbum_cert::Entity, rbum_cert::Column::CreateTime), Order::Desc)
515                                .offset(rbum_cert_conf.coexist_num as u64),
516                        )
517                        .await?;
518                    for need_delete_rbum_cert_id in need_delete_rbum_cert_ids {
519                        Self::delete_rbum(&need_delete_rbum_cert_id.id, funs, ctx).await?;
520                    }
521                }
522            }
523            if let Some(vcode) = &add_req.vcode {
524                // here we don't add cool down limit for vcode
525                Self::add_vcode_to_cache(&add_req.ak, vcode, rel_rbum_cert_conf_id, None, funs, ctx).await?;
526            }
527        }
528        Ok(())
529    }
530
531    async fn before_modify_rbum(id: &str, modify_req: &mut RbumCertModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
532        Self::check_ownership(id, funs, ctx).await?;
533        let rbum_cert = Self::peek_rbum(
534            id,
535            &RbumCertFilterReq {
536                basic: RbumBasicFilterReq {
537                    with_sub_own_paths: true,
538                    ..Default::default()
539                },
540                ..Default::default()
541            },
542            funs,
543            ctx,
544        )
545        .await?;
546        if let Some(rel_rbum_cert_conf_id) = &rbum_cert.rel_rbum_cert_conf_id {
547            if !rel_rbum_cert_conf_id.is_empty() {
548                let rbum_cert_conf = RbumCertConfServ::peek_rbum(
549                    rel_rbum_cert_conf_id,
550                    &RbumCertConfFilterReq {
551                        basic: RbumBasicFilterReq {
552                            with_sub_own_paths: true,
553                            ..Default::default()
554                        },
555                        ..Default::default()
556                    },
557                    funs,
558                    ctx,
559                )
560                .await?;
561
562                if let Some(ak) = &modify_req.ak {
563                    if !rbum_cert_conf.ak_rule.is_empty()
564                        && !Regex::new(&rbum_cert_conf.ak_rule)
565                            .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "modify", &format!("ak rule is invalid:{e}"), "400-rbum-cert-conf-ak-rule-invalid"))?
566                            .is_match(ak.as_ref())
567                            .unwrap_or(false)
568                    {
569                        return Err(funs.err().bad_request(
570                            &Self::get_obj_name(),
571                            "modify",
572                            &format!("ak {ak} is not match ak rule"),
573                            "400-rbum-cert-conf-ak-rule-not-match",
574                        ));
575                    }
576                }
577                if let Some(sk) = &modify_req.sk {
578                    if rbum_cert_conf.sk_need
579                        && !rbum_cert_conf.sk_rule.is_empty()
580                        && !Regex::new(&rbum_cert_conf.sk_rule)
581                            .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "modify", &format!("sk rule is invalid:{e}"), "400-rbum-cert-conf-sk-rule-invalid"))?
582                            .is_match(sk.as_ref())
583                            .unwrap_or(false)
584                        && !modify_req.ignore_check_sk
585                    {
586                        return Err(funs.err().bad_request(
587                            &Self::get_obj_name(),
588                            "modify",
589                            &format!("sk {sk} is not match sk rule"),
590                            "400-rbum-cert-conf-sk-rule-not-match",
591                        ));
592                    }
593                }
594                if modify_req.ak.is_some()
595                    && funs
596                        .db()
597                        .count(
598                            Query::select()
599                                .column(rbum_cert::Column::Id)
600                                .from(rbum_cert::Entity)
601                                .and_where(Expr::col(rbum_cert::Column::Ak).eq(modify_req.ak.as_ref().expect("ignore").to_string()))
602                                .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).eq(rbum_cert_conf.id.clone()))
603                                .and_where(Expr::col(rbum_cert::Column::OwnPaths).like(format!("{}%", ctx.own_paths).as_str()))
604                                .and_where(Expr::col(rbum_cert::Column::Id).ne(id.to_string().as_str())),
605                        )
606                        .await?
607                        > 0
608                {
609                    return Err(funs.err().conflict(&Self::get_obj_name(), "modify", "ak is used", "409-rbum-cert-ak-duplicate"));
610                }
611
612                // Encrypt Sk
613                if (modify_req.sk.is_some() || modify_req.ak.is_some()) && rbum_cert_conf.sk_encrypted {
614                    if modify_req.ak.is_some() && modify_req.sk.is_none() {
615                        return Err(funs.err().conflict(&Self::get_obj_name(), "modify", "sk cannot be empty", "409-rbum-cert-ak-duplicate"));
616                    }
617                    if let Some(sk) = &modify_req.sk {
618                        let sk = Self::encrypt_sk(sk, modify_req.ak.as_ref().unwrap_or(&TrimString(rbum_cert.ak)).as_ref(), rel_rbum_cert_conf_id)?;
619                        modify_req.sk = Some(TrimString(sk));
620                    }
621                }
622            }
623        }
624        Ok(())
625    }
626
627    async fn package_modify(id: &str, modify_req: &RbumCertModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<rbum_cert::ActiveModel> {
628        let mut rbum_cert = rbum_cert::ActiveModel {
629            id: Set(id.to_string()),
630            ..Default::default()
631        };
632        if let Some(ak) = &modify_req.ak {
633            rbum_cert.ak = Set(ak.to_string());
634        }
635        if let Some(sk) = &modify_req.sk {
636            rbum_cert.sk = Set(sk.to_string());
637        }
638        if let Some(sk_invisible) = &modify_req.sk_invisible {
639            rbum_cert.sk_invisible = Set(*sk_invisible);
640        }
641        if let Some(ext) = &modify_req.ext {
642            rbum_cert.ext = Set(ext.to_string());
643        }
644        if let Some(start_time) = modify_req.start_time {
645            rbum_cert.start_time = Set(start_time);
646        }
647        if let Some(end_time) = modify_req.end_time {
648            rbum_cert.end_time = Set(end_time);
649        }
650        if let Some(status) = &modify_req.status {
651            rbum_cert.status = Set(status.to_int());
652        }
653        if let Some(conn_uri) = &modify_req.conn_uri {
654            rbum_cert.conn_uri = Set(conn_uri.to_string());
655        }
656        Ok(rbum_cert)
657    }
658
659    async fn package_query(is_detail: bool, filter: &RbumCertFilterReq, _: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SelectStatement> {
660        let mut query = Query::select();
661        query
662            .columns(vec![
663                (rbum_cert::Entity, rbum_cert::Column::Id),
664                (rbum_cert::Entity, rbum_cert::Column::Kind),
665                (rbum_cert::Entity, rbum_cert::Column::Supplier),
666                (rbum_cert::Entity, rbum_cert::Column::Ak),
667                (rbum_cert::Entity, rbum_cert::Column::SkInvisible),
668                (rbum_cert::Entity, rbum_cert::Column::Ext),
669                (rbum_cert::Entity, rbum_cert::Column::StartTime),
670                (rbum_cert::Entity, rbum_cert::Column::EndTime),
671                (rbum_cert::Entity, rbum_cert::Column::ConnUri),
672                (rbum_cert::Entity, rbum_cert::Column::Status),
673                (rbum_cert::Entity, rbum_cert::Column::RelRbumCertConfId),
674                (rbum_cert::Entity, rbum_cert::Column::RelRbumKind),
675                (rbum_cert::Entity, rbum_cert::Column::RelRbumId),
676                (rbum_cert::Entity, rbum_cert::Column::OwnPaths),
677                (rbum_cert::Entity, rbum_cert::Column::Owner),
678                (rbum_cert::Entity, rbum_cert::Column::CreateTime),
679                (rbum_cert::Entity, rbum_cert::Column::UpdateTime),
680            ])
681            .expr_as(
682                Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::Name)).if_null(""),
683                Alias::new("rel_rbum_cert_conf_name"),
684            )
685            .from(rbum_cert::Entity)
686            .left_join(
687                rbum_cert_conf::Entity,
688                Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::Id)).equals((rbum_cert::Entity, rbum_cert::Column::RelRbumCertConfId)),
689            );
690        if let Some(id) = &filter.id {
691            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Id)).eq(id.to_string()));
692        }
693        if let Some(ak) = &filter.ak {
694            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Ak)).eq(ak.to_string()));
695        }
696        if let Some(ak) = &filter.ak_like {
697            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Ak)).like(format!("{ak}%")));
698        }
699        if let Some(kind) = &filter.kind {
700            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Kind)).eq(kind.to_string()));
701        }
702        if let Some(supplier) = &filter.suppliers {
703            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Supplier)).is_in::<&str, Vec<&str>>(supplier.iter().map(|s| &s[..]).collect()));
704        }
705        if let Some(status) = &filter.status {
706            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Status)).eq(status.to_int()));
707        }
708        if let Some(ext) = &filter.ext {
709            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Ext)).eq(ext.to_string()));
710        }
711        if let Some(rel_rbum_cert_conf_ids) = &filter.rel_rbum_cert_conf_ids {
712            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumCertConfId)).is_in(rel_rbum_cert_conf_ids.clone()));
713        }
714        if let Some(rel_rbum_kind) = &filter.rel_rbum_kind {
715            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumKind)).eq(rel_rbum_kind.to_int()));
716        }
717        if let Some(rel_rbum_id) = &filter.rel_rbum_id {
718            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumId)).eq(rel_rbum_id.to_string()));
719        }
720        if let Some(rel_rbum_ids) = &filter.rel_rbum_ids {
721            query.and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumId)).is_in(rel_rbum_ids.clone()));
722        }
723        query.with_filter(Self::get_table_name(), &filter.basic, is_detail, false, ctx);
724        Ok(query)
725    }
726}
727
728impl RbumCertServ {
729    /// Add dynamic sk(verification code) to cache
730    ///
731    ///
732    /// 添加动态sk(验证码)到缓存
733    pub async fn add_vcode_to_cache(ak: &str, vcode: &str, cert_conf_id: &str, cool_down_in_sec: Option<u32>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
734        let rbum_cert_conf = RbumCertConfServ::peek_rbum(cert_conf_id, &RbumCertConfFilterReq::default(), funs, ctx).await?;
735        funs.cache()
736            .set_ex(
737                format!("{}{}:{}", funs.rbum_conf_cache_key_cert_vcode_info_(), &ctx.own_paths, ak).as_str(),
738                vcode.to_string().as_str(),
739                rbum_cert_conf.expire_sec as u64,
740            )
741            .await?;
742        // check vcode cool down
743        let cool_down_key = format!("{}{}:{}:cd", funs.rbum_conf_cache_key_cert_vcode_info_(), &ctx.own_paths, ak);
744        let ttl: i32 = funs.cache().cmd().await?.ttl(&cool_down_key).await?;
745        if ttl > 0 {
746            let message = format!("vcode send still cooling down until {} secs latter", ttl);
747            return Err(funs.err().bad_request(&Self::get_obj_name(), "add_vcode_to_cache", &message, "400-rbum-cert-vcode-cool-down"));
748        } else if let Some(cool_down_in_sec) = cool_down_in_sec {
749            // set cool down key
750            funs.cache().set_ex(&cool_down_key, "", cool_down_in_sec as u64).await?;
751        }
752
753        Ok(())
754    }
755
756    /// Get dynamic sk(verification code) from cache
757    ///
758    /// 从缓存中获取动态sk(验证码)
759    pub async fn get_vcode_in_cache(ak: &str, own_paths: &str, funs: &TardisFunsInst) -> TardisResult<Option<String>> {
760        let vcode = funs.cache().get(format!("{}{}:{}", funs.rbum_conf_cache_key_cert_vcode_info_(), own_paths, ak).as_str()).await?;
761        Ok(vcode)
762    }
763
764    /// Get and delete dynamic sk(verification code) from cache
765    ///
766    /// 从缓存中获取并删除动态sk(验证码)
767    pub async fn get_and_delete_vcode_in_cache(ak: &str, own_paths: &str, funs: &TardisFunsInst) -> TardisResult<Option<String>> {
768        let vcode = funs.cache().get(format!("{}{}:{}", funs.rbum_conf_cache_key_cert_vcode_info_(), own_paths, ak).as_str()).await?;
769        if vcode.is_some() {
770            funs.cache().del(format!("{}{}:{}", funs.rbum_conf_cache_key_cert_vcode_info_(), own_paths, ak).as_str()).await?;
771        }
772        Ok(vcode)
773    }
774
775    /// Check whether the certificate is exist
776    ///
777    /// 检查凭证是否存在
778    pub async fn check_exist(ak: &str, rbum_cert_conf_id: &str, own_paths: &str, funs: &TardisFunsInst) -> TardisResult<bool> {
779        let mut query = Query::select();
780        query
781            .column(rbum_cert::Column::Id)
782            .from(rbum_cert::Entity)
783            .and_where(Expr::col(rbum_cert::Column::Ak).eq(ak))
784            .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).eq(rbum_cert_conf_id))
785            .and_where(Expr::col(rbum_cert::Column::OwnPaths).eq(own_paths))
786            .and_where(Expr::col(rbum_cert::Column::Status).eq(RbumCertStatusKind::Enabled.to_int()));
787        funs.db().count(&query).await.map(|r| r > 0)
788    }
789
790    /// Validate the validity of the certificate according to the specified certificate configuration
791    ///
792    /// 根据指定的凭证配置,验证凭证的合法性
793    ///
794    /// # Parameters
795    /// - `ak` - Access key
796    /// - `input_sk` - Secret key
797    /// - `rbum_cert_conf_id` - Certificate configuration id
798    /// - `ignore_end_time` - Whether to ignore the expiration time
799    /// - `own_paths` - Own paths
800    /// - `funs` - TardisFunsInst
801    ///
802    /// # Returns
803    /// - (the certificate id, certificate relationship type, and certificate relationship id)
804    pub async fn validate_by_spec_cert_conf(
805        ak: &str,
806        input_sk: &str,
807        rbum_cert_conf_id: &str,
808        ignore_end_time: bool,
809        own_paths: &str,
810        funs: &TardisFunsInst,
811    ) -> TardisResult<(String, RbumCertRelKind, String)> {
812        #[derive(Debug, sea_orm::FromQueryResult)]
813        struct IdAndSkResp {
814            pub id: String,
815            pub sk: String,
816            pub rel_rbum_kind: RbumCertRelKind,
817            pub rel_rbum_id: String,
818            pub end_time: DateTime<Utc>,
819        }
820
821        #[derive(Debug, sea_orm::FromQueryResult)]
822        struct CertConfPeekResp {
823            pub sk_encrypted: bool,
824            pub sk_dynamic: bool,
825            pub sk_lock_cycle_sec: i32,
826            pub sk_lock_err_times: i16,
827            pub sk_lock_duration_sec: i32,
828        }
829        let mut query = Query::select();
830        query
831            .column(rbum_cert::Column::Id)
832            .column(rbum_cert::Column::Sk)
833            .column(rbum_cert::Column::RelRbumKind)
834            .column(rbum_cert::Column::RelRbumId)
835            .column(rbum_cert::Column::EndTime)
836            .from(rbum_cert::Entity)
837            .and_where(Expr::col(rbum_cert::Column::Ak).eq(ak))
838            .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).eq(rbum_cert_conf_id))
839            .and_where(Expr::col(rbum_cert::Column::OwnPaths).eq(own_paths))
840            .and_where(Expr::col(rbum_cert::Column::Status).ne(RbumCertStatusKind::Disabled.to_int()))
841            .and_where(Expr::col(rbum_cert::Column::StartTime).lte(Utc::now().naive_utc()));
842        let rbum_cert = funs.db().get_dto::<IdAndSkResp>(&query).await?;
843        if let Some(rbum_cert) = rbum_cert {
844            if Self::cert_is_locked(&rbum_cert.rel_rbum_id, funs).await? {
845                return Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "cert is locked", "400-rbum-cert-lock"));
846            }
847            if !ignore_end_time && rbum_cert.end_time < Utc::now() {
848                return Err(funs.err().conflict(&Self::get_obj_name(), "valid", "sk is expired", "409-rbum-cert-sk-expire"));
849            }
850            let cert_conf_peek_resp = funs
851                .db()
852                .get_dto::<CertConfPeekResp>(
853                    Query::select()
854                        .column(rbum_cert_conf::Column::SkEncrypted)
855                        .column(rbum_cert_conf::Column::SkDynamic)
856                        .column(rbum_cert_conf::Column::SkLockCycleSec)
857                        .column(rbum_cert_conf::Column::SkLockErrTimes)
858                        .column(rbum_cert_conf::Column::SkLockDurationSec)
859                        .from(rbum_cert_conf::Entity)
860                        .and_where(Expr::col(rbum_cert_conf::Column::Id).eq(rbum_cert_conf_id)),
861                )
862                .await?
863                .ok_or_else(|| funs.err().not_found(&Self::get_obj_name(), "valid", "not found cert conf", "404-rbum-cert-conf-not-exist"))?;
864            let input_sk = if cert_conf_peek_resp.sk_encrypted {
865                Self::encrypt_sk(input_sk, ak, rbum_cert_conf_id)?
866            } else {
867                input_sk.to_string()
868            };
869            let storage_sk = if cert_conf_peek_resp.sk_dynamic {
870                if let Some(cached_vcode) = Self::get_vcode_in_cache(ak, own_paths, funs).await? {
871                    cached_vcode
872                } else {
873                    log::warn!(
874                        "validation error [vcode is not exist] by ak {},rbum_cert_conf_id {}, own_paths {}",
875                        ak,
876                        rbum_cert_conf_id,
877                        own_paths
878                    );
879                    Self::after_validate_fail(
880                        &rbum_cert.rel_rbum_id,
881                        cert_conf_peek_resp.sk_lock_cycle_sec,
882                        cert_conf_peek_resp.sk_lock_err_times,
883                        cert_conf_peek_resp.sk_lock_duration_sec,
884                        funs,
885                    )
886                    .await?;
887                    return Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-cert-valid-error"));
888                }
889            } else {
890                rbum_cert.sk
891            };
892            if storage_sk == input_sk {
893                Self::after_validate_success(&rbum_cert.rel_rbum_id, funs).await?;
894                Ok((rbum_cert.id, rbum_cert.rel_rbum_kind, rbum_cert.rel_rbum_id))
895            } else {
896                log::warn!(
897                    "validation error [sk is not match] by ak {},rbum_cert_conf_id {}, own_paths {}",
898                    ak,
899                    rbum_cert_conf_id,
900                    own_paths
901                );
902                Self::after_validate_fail(
903                    &rbum_cert.rel_rbum_id,
904                    cert_conf_peek_resp.sk_lock_cycle_sec,
905                    cert_conf_peek_resp.sk_lock_err_times,
906                    cert_conf_peek_resp.sk_lock_duration_sec,
907                    funs,
908                )
909                .await?;
910                Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-cert-valid-error"))
911            }
912        } else {
913            log::warn!("validation error by ak {},rbum_cert_conf_id {}, own_paths {}", ak, rbum_cert_conf_id, own_paths);
914            Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-cert-valid-error"))
915        }
916    }
917
918    /// Validate the validity of the certificate using the current or basic certificate configuration
919    ///
920    /// 使用当前或基础凭证配置,验证凭证的合法性
921    ///
922    /// # Parameters
923    /// - `ak` - Access key
924    /// - `input_sk` - Secret key
925    /// - `rel_rbum_kind` - Certificate relationship type
926    /// - `ignore_end_time` - Whether to ignore the expiration time
927    /// - `own_paths` - Own paths
928    /// - `allowed_kinds` - Allowed certificate configuration types
929    /// - `funs` - TardisFunsInst
930    ///
931    /// # Returns
932    /// - (the certificate id, certificate relationship type, and certificate relationship id)
933    pub async fn validate_by_ak_and_basic_sk(
934        ak: &str,
935        input_sk: &str,
936        rel_rbum_kind: &RbumCertRelKind,
937        ignore_end_time: bool,
938        own_paths: Option<String>,
939        allowed_kinds: Vec<&str>,
940        funs: &TardisFunsInst,
941    ) -> TardisResult<(String, RbumCertRelKind, String)> {
942        #[derive(Debug, sea_orm::FromQueryResult)]
943        struct IdAndSkResp {
944            pub id: String,
945            pub sk: String,
946            pub rel_rbum_id: String,
947            pub rel_rbum_cert_conf_id: String,
948            pub end_time: DateTime<Utc>,
949        }
950
951        #[derive(Debug, sea_orm::FromQueryResult)]
952        struct CertConfPeekResp {
953            pub is_basic: bool,
954            pub sk_encrypted: bool,
955            pub rel_rbum_domain_id: String,
956            pub rel_rbum_item_id: String,
957            pub sk_lock_cycle_sec: i32,
958            pub sk_lock_err_times: i16,
959            pub sk_lock_duration_sec: i32,
960        }
961        let mut query = Query::select();
962        query
963            .column(rbum_cert::Column::Id)
964            .column(rbum_cert::Column::Sk)
965            .column(rbum_cert::Column::RelRbumId)
966            .column(rbum_cert::Column::EndTime)
967            .column(rbum_cert::Column::RelRbumCertConfId)
968            .from(rbum_cert::Entity)
969            .and_where(Expr::col(rbum_cert::Column::Ak).eq(ak))
970            .and_where(Expr::col(rbum_cert::Column::RelRbumKind).eq(rel_rbum_kind.to_int()))
971            // Exclude disabled state, have enabled and pending
972            .and_where(Expr::col(rbum_cert::Column::Status).ne(RbumCertStatusKind::Disabled.to_int()))
973            .and_where(Expr::col(rbum_cert::Column::StartTime).lte(Utc::now().naive_utc()))
974            //basic sk must have cert conf
975            .and_where(Expr::col(rbum_cert::Column::RelRbumCertConfId).ne(""));
976        if let Some(own_paths) = own_paths.clone() {
977            query.and_where(Expr::col(rbum_cert::Column::OwnPaths).eq(own_paths));
978        }
979        let rbum_cert = funs.db().get_dto::<IdAndSkResp>(&query).await?;
980        if let Some(rbum_cert) = rbum_cert {
981            if Self::cert_is_locked(&rbum_cert.rel_rbum_id, funs).await? {
982                return Err(funs.err().unauthorized(&Self::get_obj_name(), "valid_lock", "cert is locked", "401-rbum-cert-lock"));
983            }
984            if !ignore_end_time && rbum_cert.end_time < Utc::now() {
985                return Err(funs.err().conflict(&Self::get_obj_name(), "valid", "sk is expired", "409-rbum-cert-sk-expire"));
986            }
987            if !rbum_cert.rel_rbum_cert_conf_id.is_empty() {
988                let cert_conf_peek_resp = funs
989                    .db()
990                    .get_dto::<CertConfPeekResp>(
991                        Query::select()
992                            .column(rbum_cert_conf::Column::IsBasic)
993                            .column(rbum_cert_conf::Column::RelRbumDomainId)
994                            .column(rbum_cert_conf::Column::RelRbumItemId)
995                            .column(rbum_cert_conf::Column::SkEncrypted)
996                            .column(rbum_cert_conf::Column::SkLockCycleSec)
997                            .column(rbum_cert_conf::Column::SkLockErrTimes)
998                            .column(rbum_cert_conf::Column::SkLockDurationSec)
999                            .from(rbum_cert_conf::Entity)
1000                            .and_where(Expr::col(rbum_cert_conf::Column::Id).eq(rbum_cert.rel_rbum_cert_conf_id.as_str()))
1001                            .and_where(Expr::col(rbum_cert_conf::Column::Kind).is_in(allowed_kinds)),
1002                    )
1003                    .await?
1004                    .ok_or_else(|| funs.err().not_found(&Self::get_obj_name(), "valid", "not found cert conf", "404-rbum-cert-conf-not-exist"))?;
1005                let verify_input_sk = if cert_conf_peek_resp.sk_encrypted {
1006                    Self::encrypt_sk(input_sk, ak, rbum_cert.rel_rbum_cert_conf_id.as_str())?
1007                } else {
1008                    input_sk.to_string()
1009                };
1010                if rbum_cert.sk == verify_input_sk {
1011                    Self::after_validate_success(&rbum_cert.rel_rbum_id, funs).await?;
1012                    Ok((rbum_cert.id, rel_rbum_kind.clone(), rbum_cert.rel_rbum_id))
1013                } else if !cert_conf_peek_resp.is_basic {
1014                    // Ok(...) ?
1015                    Ok(Self::validate_by_non_basic_cert_conf_with_basic_sk(
1016                        rbum_cert.rel_rbum_id.as_str(),
1017                        cert_conf_peek_resp.rel_rbum_domain_id.as_str(),
1018                        cert_conf_peek_resp.rel_rbum_item_id.as_str(),
1019                        input_sk,
1020                        ignore_end_time,
1021                        funs,
1022                    )
1023                    .await?)
1024                } else {
1025                    log::warn!(
1026                        "validation error [sk is not match] by ak {},rel_rbum_cert_conf_id {}, own_paths {:?}",
1027                        ak,
1028                        rbum_cert.rel_rbum_cert_conf_id,
1029                        own_paths
1030                    );
1031                    Self::after_validate_fail(
1032                        &rbum_cert.rel_rbum_id,
1033                        cert_conf_peek_resp.sk_lock_cycle_sec,
1034                        cert_conf_peek_resp.sk_lock_err_times,
1035                        cert_conf_peek_resp.sk_lock_duration_sec,
1036                        funs,
1037                    )
1038                    .await?;
1039                    Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-usrpwd-cert-valid-error"))
1040                }
1041            } else {
1042                log::warn!("validation error by ak {},rbum_cert_conf_id is None, own_paths {:?}", ak, own_paths);
1043                Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-usrpwd-cert-valid-error"))
1044            }
1045        } else {
1046            log::warn!("validation error by ak {},rel_rbum_kind {}, own_paths {:?}", ak, rel_rbum_kind, own_paths);
1047            Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "validation error", "401-rbum-usrpwd-cert-valid-error"))
1048        }
1049    }
1050
1051    /// Validate the validity of the certificate using the basic certificate configuration
1052    ///
1053    /// 使用基础凭证配置,验证凭证的合法性
1054    ///
1055    /// # Parameters
1056    /// - `cert_rel_rbum_id` - Certificate relationship id
1057    /// - `cert_conf_rel_rbum_domain_id` - Certificate configuration relationship domain id
1058    /// - `cert_conf_rel_rbum_item_id` - Certificate configuration relationship item id
1059    /// - `input_sk` - Secret key
1060    /// - `ignore_end_time` - Whether to ignore the expiration time
1061    /// - `funs` - TardisFunsInst
1062    ///
1063    /// # Returns
1064    /// - (the certificate id, certificate relationship type, and certificate relationship id)
1065    async fn validate_by_non_basic_cert_conf_with_basic_sk(
1066        cert_rel_rbum_id: &str,
1067        cert_conf_rel_rbum_domain_id: &str,
1068        cert_conf_rel_rbum_item_id: &str,
1069        input_sk: &str,
1070        ignore_end_time: bool,
1071        funs: &TardisFunsInst,
1072    ) -> TardisResult<(String, RbumCertRelKind, String)> {
1073        #[derive(Debug, sea_orm::FromQueryResult)]
1074        struct BasicCertInfoResp {
1075            pub id: String,
1076            pub ak: String,
1077            pub sk: String,
1078            pub rel_rbum_kind: RbumCertRelKind,
1079            pub end_time: DateTime<Utc>,
1080            pub sk_encrypted: bool,
1081            pub rel_rbum_cert_conf_id: String,
1082            pub sk_lock_cycle_sec: i32,
1083            pub sk_lock_err_times: i16,
1084            pub sk_lock_duration_sec: i32,
1085        }
1086        let rbum_basic_cert_info_resp = funs
1087            .db()
1088            .get_dto::<BasicCertInfoResp>(
1089                Query::select()
1090                    .expr_as(Expr::col((rbum_cert::Entity, rbum_cert::Column::Id)).if_null(""), Alias::new("id"))
1091                    .column(rbum_cert::Column::Ak)
1092                    .column(rbum_cert::Column::Sk)
1093                    .column(rbum_cert::Column::RelRbumKind)
1094                    .column(rbum_cert::Column::EndTime)
1095                    .column(rbum_cert::Column::RelRbumCertConfId)
1096                    .column(rbum_cert_conf::Column::SkEncrypted)
1097                    .column(rbum_cert_conf::Column::SkLockCycleSec)
1098                    .column(rbum_cert_conf::Column::SkLockErrTimes)
1099                    .column(rbum_cert_conf::Column::SkLockDurationSec)
1100                    .from(rbum_cert::Entity)
1101                    .inner_join(
1102                        rbum_cert_conf::Entity,
1103                        Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::Id)).equals((rbum_cert::Entity, rbum_cert::Column::RelRbumCertConfId)),
1104                    )
1105                    .and_where(Expr::col(rbum_cert::Column::RelRbumId).eq(cert_rel_rbum_id))
1106                    .and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumDomainId)).eq(cert_conf_rel_rbum_domain_id))
1107                    .and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::RelRbumItemId)).eq(cert_conf_rel_rbum_item_id))
1108                    .and_where(Expr::col((rbum_cert_conf::Entity, rbum_cert_conf::Column::IsBasic)).eq(true)),
1109            )
1110            .await?
1111            .ok_or_else(|| funs.err().not_found(&Self::get_obj_name(), "valid", "not found basic cert conf", "404-rbum-cert-conf-not-exist"))?;
1112        if !ignore_end_time && rbum_basic_cert_info_resp.end_time < Utc::now() {
1113            return Err(funs.err().conflict(&Self::get_obj_name(), "valid", "basic sk is expired", "409-rbum-cert-sk-expire"));
1114        }
1115        let verify_input_sk = if rbum_basic_cert_info_resp.sk_encrypted {
1116            Self::encrypt_sk(input_sk, &rbum_basic_cert_info_resp.ak, &rbum_basic_cert_info_resp.rel_rbum_cert_conf_id)?
1117        } else {
1118            input_sk.to_string()
1119        };
1120
1121        if rbum_basic_cert_info_resp.sk == verify_input_sk {
1122            Self::after_validate_success(cert_rel_rbum_id, funs).await?;
1123            Ok((rbum_basic_cert_info_resp.id, rbum_basic_cert_info_resp.rel_rbum_kind, cert_rel_rbum_id.to_string()))
1124        } else {
1125            log::warn!(
1126                "validation error [sk is not match] by ak {},rbum_cert_conf_id {}, rel_rbum_id {}",
1127                rbum_basic_cert_info_resp.ak,
1128                rbum_basic_cert_info_resp.rel_rbum_cert_conf_id,
1129                cert_rel_rbum_id
1130            );
1131            Self::after_validate_fail(
1132                cert_rel_rbum_id,
1133                rbum_basic_cert_info_resp.sk_lock_cycle_sec,
1134                rbum_basic_cert_info_resp.sk_lock_err_times,
1135                rbum_basic_cert_info_resp.sk_lock_duration_sec,
1136                funs,
1137            )
1138            .await?;
1139            Err(funs.err().unauthorized(&Self::get_obj_name(), "valid", "basic validation error", "401-rbum-cert-valid-error"))
1140        }
1141    }
1142
1143    /// Show sk
1144    ///
1145    /// 显示sk
1146    pub async fn show_sk(id: &str, filter: &RbumCertFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<String> {
1147        #[derive(sea_orm::FromQueryResult)]
1148        struct SkResp {
1149            pub sk: String,
1150        }
1151        let mut query = Query::select();
1152        query.column((rbum_cert::Entity, rbum_cert::Column::Sk)).from(rbum_cert::Entity).and_where(Expr::col((rbum_cert::Entity, rbum_cert::Column::Id)).eq(id)).with_filter(
1153            Self::get_table_name(),
1154            &filter.basic,
1155            false,
1156            false,
1157            ctx,
1158        );
1159        let sk_resp = funs.db().get_dto::<SkResp>(&query).await?;
1160        if let Some(sk_resp) = sk_resp {
1161            Ok(sk_resp.sk)
1162        } else {
1163            Err(funs.err().not_found(&Self::get_obj_name(), "show_sk", "not found cert record", "404-rbum-*-obj-not-exist"))
1164        }
1165    }
1166
1167    /// Reset sk
1168    ///
1169    /// 重置sk
1170    pub async fn reset_sk(id: &str, new_sk: &str, is_ignore_check_sk: bool, filter: &RbumCertFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1171        let rbum_cert = Self::peek_rbum(id, filter, funs, ctx).await?;
1172        let mut repeatable = true;
1173        let new_sk = if let Some(rel_rbum_cert_conf_id) = &rbum_cert.rel_rbum_cert_conf_id {
1174            let rbum_cert_conf = RbumCertConfServ::peek_rbum(
1175                rel_rbum_cert_conf_id,
1176                &RbumCertConfFilterReq {
1177                    basic: filter.basic.clone(),
1178                    ..Default::default()
1179                },
1180                funs,
1181                ctx,
1182            )
1183            .await?;
1184            repeatable = rbum_cert_conf.repeatable;
1185            if !rbum_cert_conf.sk_rule.is_empty()
1186                && !Regex::new(&rbum_cert_conf.sk_rule)
1187                    .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "reset_sk", &format!("sk rule is invalid:{e}"), "400-rbum-cert-conf-sk-rule-invalid"))?
1188                    .is_match(new_sk)
1189                    .unwrap_or(false)
1190                && !is_ignore_check_sk
1191            {
1192                return Err(funs.err().bad_request(
1193                    &Self::get_obj_name(),
1194                    "reset_sk",
1195                    &format!("sk {new_sk} is not match sk rule"),
1196                    "400-rbum-cert-conf-sk-rule-not-match",
1197                ));
1198            }
1199            if rbum_cert_conf.sk_encrypted {
1200                Self::encrypt_sk(new_sk, &rbum_cert.ak, rel_rbum_cert_conf_id)?
1201            } else {
1202                new_sk.to_string()
1203            }
1204        } else {
1205            new_sk.to_string()
1206        };
1207        let stored_sk = Self::show_sk(id, filter, funs, ctx).await?;
1208        if new_sk == stored_sk && !repeatable {
1209            return Err(funs.err().bad_request(&Self::get_obj_name(), "reset_sk", &format!("sk {new_sk} is duplicate"), "400-rbum-cert-reset-sk-duplicate"));
1210        }
1211        funs.db()
1212            .update_one(
1213                rbum_cert::ActiveModel {
1214                    id: Set(id.to_string()),
1215                    sk: Set(new_sk),
1216                    ..Default::default()
1217                },
1218                ctx,
1219            )
1220            .await?;
1221        Ok(())
1222    }
1223
1224    /// Change sk
1225    ///
1226    /// 更改sk
1227    pub async fn change_sk(id: &str, original_sk: &str, input_sk: &str, filter: &RbumCertFilterReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
1228        let rbum_cert = Self::peek_rbum(id, filter, funs, ctx).await?;
1229        let stored_sk = Self::show_sk(id, filter, funs, ctx).await?;
1230        if input_sk.to_lowercase().contains(rbum_cert.ak.to_lowercase().as_str()) {
1231            return Err(funs.err().bad_request(&Self::get_obj_name(), "change_sk", "sk can not contain ak", "400-rbum-cert-sk-contains-ak"));
1232        }
1233        let (new_sk, end_time) = if let Some(rel_rbum_cert_conf_id) = &rbum_cert.rel_rbum_cert_conf_id {
1234            let rbum_cert_conf = RbumCertConfServ::peek_rbum(rel_rbum_cert_conf_id, &RbumCertConfFilterReq::default(), funs, ctx).await?;
1235            let original_sk = if rbum_cert_conf.sk_encrypted {
1236                Self::encrypt_sk(original_sk, &rbum_cert.ak, &rbum_cert_conf.id)?
1237            } else {
1238                original_sk.to_string()
1239            };
1240            if original_sk != stored_sk {
1241                return Err(funs.err().unauthorized(&Self::get_obj_name(), "change_sk", "sk not match", "401-rbum-cert-ori-sk-not-match"));
1242            }
1243            if !rbum_cert_conf.sk_rule.is_empty()
1244                && !Regex::new(&rbum_cert_conf.sk_rule)
1245                    .map_err(|e| funs.err().bad_request(&Self::get_obj_name(), "change_sk", &format!("sk rule is invalid:{e}"), "400-rbum-cert-conf-sk-rule-invalid"))?
1246                    .is_match(input_sk)
1247                    .unwrap_or(false)
1248            {
1249                return Err(funs.err().bad_request(
1250                    &Self::get_obj_name(),
1251                    "change_sk",
1252                    &format!("sk {input_sk} is not match sk rule"),
1253                    "400-rbum-cert-conf-sk-rule-not-match",
1254                ));
1255            }
1256            let new_sk = if rbum_cert_conf.sk_encrypted {
1257                Self::encrypt_sk(input_sk, &rbum_cert.ak, &rbum_cert_conf.id)?
1258            } else {
1259                input_sk.to_string()
1260            };
1261            if !rbum_cert_conf.repeatable && original_sk == new_sk {
1262                return Err(funs.err().bad_request(
1263                    &Self::get_obj_name(),
1264                    "change_sk",
1265                    &format!("sk {input_sk} cannot be duplicated"),
1266                    "400-rbum-cert-ak-duplicate",
1267                ));
1268            }
1269            let end_time = Utc::now() + Duration::try_seconds(rbum_cert_conf.expire_sec).unwrap_or(TimeDelta::max_value());
1270            (new_sk, end_time)
1271        } else {
1272            if original_sk != stored_sk {
1273                return Err(funs.err().unauthorized(&Self::get_obj_name(), "change_sk", "sk not match", "401-rbum-cert-ori-sk-not-match"));
1274            }
1275            (input_sk.to_string(), rbum_cert.start_time + (rbum_cert.end_time - rbum_cert.start_time))
1276        };
1277        funs.db()
1278            .update_one(
1279                rbum_cert::ActiveModel {
1280                    id: Set(id.to_string()),
1281                    sk: Set(new_sk),
1282                    end_time: Set(end_time),
1283                    ..Default::default()
1284                },
1285                ctx,
1286            )
1287            .await?;
1288        Ok(())
1289    }
1290
1291    /// Encrypt sk
1292    ///
1293    /// 加密sk
1294    fn encrypt_sk(sk: &str, ak: &str, rbum_cert_conf_id: &str) -> TardisResult<String> {
1295        TardisFuns::crypto.digest.sha512(format!("{sk}-{ak}-{rbum_cert_conf_id}").as_str())
1296    }
1297
1298    /// Processing logic after verification is successful
1299    ///
1300    /// 验证成功后的处理逻辑
1301    async fn after_validate_success(rbum_item_id: &str, funs: &TardisFunsInst) -> TardisResult<()> {
1302        funs.cache().del(&format!("{}{}", funs.rbum_conf_cache_key_cert_err_times_(), rbum_item_id)).await?;
1303        Ok(())
1304    }
1305
1306    /// Processing logic after verification fails
1307    ///
1308    /// 验证失败后的处理逻辑
1309    async fn after_validate_fail(rbum_item_id: &str, sk_lock_cycle_sec: i32, sk_lock_err_times: i16, sk_lock_duration_sec: i32, funs: &TardisFunsInst) -> TardisResult<()> {
1310        if sk_lock_cycle_sec == 0 || sk_lock_err_times == 0 || sk_lock_duration_sec == 0 {
1311            return Ok(());
1312        }
1313        let err_times = funs.cache().incr(&format!("{}{}", funs.rbum_conf_cache_key_cert_err_times_(), rbum_item_id), 1).await?;
1314        if sk_lock_err_times <= err_times as i16 {
1315            funs.cache().set_ex(&format!("{}{}", funs.rbum_conf_cache_key_cert_locked_(), rbum_item_id), "", sk_lock_duration_sec as u64).await?;
1316            funs.cache().del(&format!("{}{}", funs.rbum_conf_cache_key_cert_err_times_(), rbum_item_id)).await?;
1317        } else if err_times == 1 {
1318            funs.cache().expire(&format!("{}{}", funs.rbum_conf_cache_key_cert_err_times_(), rbum_item_id), sk_lock_cycle_sec as i64).await?;
1319        }
1320        Ok(())
1321    }
1322
1323    /// Check whether the certificate is locked
1324    ///
1325    /// 检查证书是否被锁定
1326    pub async fn cert_is_locked(rel_rbum_id: &str, funs: &TardisFunsInst) -> TardisResult<bool> {
1327        let result = funs
1328            .cache()
1329            .exists(&format!("{}{}", funs.rbum_conf_cache_key_cert_locked_(), rel_rbum_id))
1330            .await
1331            .map_err(|e| funs.err().unauthorized(&Self::get_obj_name(), "cert_is_locked", &e.to_string(), "400-rbum-cert-lock"))?;
1332        Ok(result)
1333    }
1334}