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 {
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 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 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 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 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 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 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 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 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 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 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 funs.cache().set_ex(&cool_down_key, "", cool_down_in_sec as u64).await?;
751 }
752
753 Ok(())
754 }
755
756 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 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 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 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 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 .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 .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(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 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 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 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 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 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 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 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 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}