diff --git a/src/application/datakey.rs b/src/application/datakey.rs index 01903c06f2fbdd0c32cb3f9b396338e70ae8224a..9f3104178c3ed182aca95fbe8296b0d7b0b6c8e9 100644 --- a/src/application/datakey.rs +++ b/src/application/datakey.rs @@ -18,7 +18,7 @@ use crate::domain::datakey::repository::Repository as DatakeyRepository; use crate::domain::sign_service::SignBackend; use crate::util::error::{Error, Result}; use async_trait::async_trait; -use crate::domain::datakey::entity::{DataKey, KeyAction, KeyState, KeyType, PagedDatakey, Visibility, X509CRL, X509RevokeReason}; +use crate::domain::datakey::entity::{DataKey, DatakeyPaginationQuery, KeyAction, KeyState, KeyType, PagedDatakey, Visibility, X509CRL, X509RevokeReason}; use tokio::time::{self}; use crate::util::cache::TimedFixedSizeCache; @@ -36,7 +36,7 @@ pub trait KeyService: Send + Sync{ async fn import(&self, data: &mut DataKey) -> Result; async fn get_raw_key_by_name(&self, name: &str) -> Result; - async fn get_all(&self, key_type: Option, visibility: Visibility, user_id: i32, page_size: u64, page_number: u64) -> Result; + async fn get_all(&self, user_id: i32, query: DatakeyPaginationQuery) -> Result; async fn get_one(&self, user: Option, id_or_name: String) -> Result; //get keys content async fn export_one(&self, user: Option, id_or_name: String) -> Result; @@ -212,8 +212,8 @@ where self.repository.get_by_id_or_name(None, Some(name.to_owned()), true).await } - async fn get_all(&self, key_type: Option, visibility: Visibility, user_id: i32, page_size: u64, page_number: u64) -> Result { - self.repository.get_all_keys(key_type, visibility, user_id, page_size, page_number).await + async fn get_all(&self, user_id: i32, query: DatakeyPaginationQuery) -> Result { + self.repository.get_all_keys( user_id, query).await } async fn get_one(&self, user: Option, id_or_name: String) -> Result { diff --git a/src/domain/datakey/entity.rs b/src/domain/datakey/entity.rs index 532df3ef4f6a8a4b55caa4488d1a206bed8a5ff1..fcbcbd8f7957cc9e71296a3a43b7550b50f11584 100644 --- a/src/domain/datakey/entity.rs +++ b/src/domain/datakey/entity.rs @@ -275,6 +275,16 @@ pub struct PagedDatakey { pub meta: PagedMeta } +#[derive(Debug, Clone)] +pub struct DatakeyPaginationQuery { + pub page_size: u64, + pub page_number: u64, + pub name: Option, + pub description: Option, + pub key_type: Option, + pub visibility: Option, +} + impl ExtendableAttributes for DataKey { type Item = HashMap; diff --git a/src/domain/datakey/repository.rs b/src/domain/datakey/repository.rs index fa36a441450a5e141c577919626262d4fca5987e..cabbc219ecbf38f9411955a2d91b9065802b1cdf 100644 --- a/src/domain/datakey/repository.rs +++ b/src/domain/datakey/repository.rs @@ -18,13 +18,13 @@ use super::entity::DataKey; use crate::util::error::Result; use async_trait::async_trait; use chrono::Duration; -use crate::domain::datakey::entity::{KeyState, KeyType, PagedDatakey, RevokedKey, Visibility, X509CRL, X509RevokeReason}; +use crate::domain::datakey::entity::{DatakeyPaginationQuery, KeyState, PagedDatakey, RevokedKey, X509CRL, X509RevokeReason}; #[async_trait] pub trait Repository: Send + Sync { async fn create(&self, data_key: DataKey) -> Result; async fn delete(&self, id: i32) -> Result<()>; - async fn get_all_keys(&self, key_type: Option, visibility: Visibility, user_id: i32, page_size: u64, page_number: u64) -> Result; + async fn get_all_keys(&self, user_id: i32, query: DatakeyPaginationQuery) -> Result; async fn get_by_id_or_name(&self, id: Option, name: Option, raw_datakey: bool) -> Result; async fn update_state(&self, id: i32, state: KeyState) -> Result<()>; async fn update_key_data(&self, data_key: DataKey) -> Result<()>; diff --git a/src/infra/database/model/datakey/repository.rs b/src/infra/database/model/datakey/repository.rs index 4656148083c319706230f220e60aa1783ff526c2..32f9ba9f4880e5245b75d7c9897786e16c3c769f 100644 --- a/src/infra/database/model/datakey/repository.rs +++ b/src/infra/database/model/datakey/repository.rs @@ -19,7 +19,7 @@ use super::super::user::dto as user_dto; use super::super::request_delete::dto as request_dto; use super::super::x509_crl_content::dto as crl_content_dto; use super::super::x509_revoked_key::dto as revoked_key_dto; -use crate::domain::datakey::entity::{DataKey, KeyState, KeyType, PagedDatakey, PagedMeta, ParentKey, RevokedKey, Visibility, X509CRL, X509RevokeReason}; +use crate::domain::datakey::entity::{DataKey, DatakeyPaginationQuery, KeyState, KeyType, PagedDatakey, PagedMeta, ParentKey, RevokedKey, Visibility, X509CRL, X509RevokeReason}; use crate::domain::datakey::repository::Repository; use crate::util::error::{Error, Result}; use async_trait::async_trait; @@ -147,16 +147,22 @@ impl<'a> Repository for DataKeyRepository<'a> { Ok(()) } - async fn get_all_keys(&self, key_type: Option, visibility: Visibility, user_id: i32, page_size: u64, page_number: u64) -> Result { - let mut conditions = Condition::all().add( - datakey_dto::Column::KeyState.ne(KeyState::Deleted.to_string())).add( - datakey_dto::Column::Visibility.eq(visibility.to_string()) - ); - if let Some(k_type) = key_type { - conditions = conditions.add(datakey_dto::Column::KeyType.eq(k_type.to_string())) + async fn get_all_keys(&self, user_id: i32, query: DatakeyPaginationQuery) -> Result { + let mut conditions = Condition::all().add(datakey_dto::Column::KeyState.ne(KeyState::Deleted.to_string())); + if let Some(name) = query.name { + conditions = conditions.add(datakey_dto::Column::Name.like(format!("%{}%", name))) + } + if let Some(desc) = query.description { + conditions = conditions.add(datakey_dto::Column::Description.like(format!("%{}%", desc))) } - if visibility == Visibility::Private { - conditions = conditions.add(datakey_dto::Column::User.eq(user_id)) + if let Some(k_type) = query.key_type { + conditions = conditions.add(datakey_dto::Column::KeyType.eq(k_type)) + } + if let Some(visibility) = query.visibility { + conditions = conditions.add(datakey_dto::Column::Visibility.eq(visibility.clone())); + if visibility == Visibility::Private.to_string() { + conditions = conditions.add(datakey_dto::Column::User.eq(user_id)) + } } let paginator = datakey_dto::Entity::find().select_only().columns( datakey_dto::Column::iter().filter(|col| @@ -168,10 +174,10 @@ impl<'a> Repository for DataKeyRepository<'a> { JoinType::LeftJoin, self.get_pending_operation_relation(RequestType::Delete).into(), Alias::new("request_delete_table")).join_as_rev( JoinType::LeftJoin, self.get_pending_operation_relation(RequestType::Revoke).into(), - Alias::new("request_revoke_table")).group_by(datakey_dto::Column::Id).filter(conditions).paginate(self.db_connection, page_size); + Alias::new("request_revoke_table")).group_by(datakey_dto::Column::Id).filter(conditions).paginate(self.db_connection, query.page_size); let total_numbers = paginator.num_items().await?; let mut results = vec![]; - for dto in paginator.fetch_page(page_number).await?.into_iter() { + for dto in paginator.fetch_page(query.page_number - 1).await?.into_iter() { results.push(DataKey::try_from(dto)?); } Ok(PagedDatakey{ diff --git a/src/presentation/handler/control/datakey_handler.rs b/src/presentation/handler/control/datakey_handler.rs index e9d375883ee1d32131642732c42c2af3c53d79e6..6c1d56eda6301ff8aad3e12f8494c92a4e8be9cb 100644 --- a/src/presentation/handler/control/datakey_handler.rs +++ b/src/presentation/handler/control/datakey_handler.rs @@ -24,7 +24,7 @@ use crate::presentation::handler::control::model::datakey::dto::{CertificateCont use crate::util::error::Error; use validator::Validate; use crate::application::datakey::KeyService; -use crate::domain::datakey::entity::{DataKey, KeyType, Visibility, X509RevokeReason}; +use crate::domain::datakey::entity::{DataKey, DatakeyPaginationQuery, KeyType, Visibility, X509RevokeReason}; use crate::util::key::get_datakey_full_name; use super::model::user::dto::UserIdentity; @@ -146,12 +146,9 @@ async fn create_data_key(user: UserIdentity, key_service: web::Data, key: web::Query) -> Result { key.validate()?; - let key_type = match key.key_type { - Some(ref k) => Some(KeyType::from_str(k)?), - None => None, - }; - let visibility = Visibility::from_parameter(key.visibility.clone())?; - let keys = key_service.into_inner().get_all(key_type, visibility, user.id, key.page_size, key.page_number).await?; + //test visibility matched. + Visibility::from_parameter(key.visibility.clone())?; + let keys = key_service.into_inner().get_all(user.id, DatakeyPaginationQuery::from(key.into_inner())).await?; Ok(HttpResponse::Ok().json(PagedDatakeyDTO::try_from(keys)?)) } diff --git a/src/presentation/handler/control/model/datakey/dto.rs b/src/presentation/handler/control/model/datakey/dto.rs index c9dcbb08af774b7d6e961f215b341e804d82018c..666046c3d2c78e4777bc51e4164782ae081919c5 100644 --- a/src/presentation/handler/control/model/datakey/dto.rs +++ b/src/presentation/handler/control/model/datakey/dto.rs @@ -1,4 +1,4 @@ -use crate::domain::datakey::entity::{DataKey, KeyState, PagedDatakey, Visibility, X509CRL}; +use crate::domain::datakey::entity::{DataKey, DatakeyPaginationQuery, KeyState, PagedDatakey, Visibility, X509CRL}; use crate::domain::datakey::entity::KeyType; use crate::util::error::Result; use chrono::{DateTime, Utc}; @@ -69,16 +69,34 @@ pub struct NameIdenticalQuery { #[derive(Deserialize, IntoParams, Validate, ToSchema)] pub struct ListKeyQuery { - /// Key type, optional, should be one of x509ca, x509ica, x509ee, or pgp + /// Filter by key type, optional, x509ca, x509ica, x509ee, or pgp, exact match pub key_type: Option, - /// public or private + /// Filter by visibility, optional, public or private, exact match pub visibility: Option, + /// Filter by key name, fuzzy match + pub name: Option, + /// Filter by description, fuzzy match + pub description: Option, /// the request page size, min 10, max 100 #[validate(range(min = 10, max = 100))] pub page_size: u64, - /// the request page index, starts from 0, max 1000 - #[validate(range(min = 0, max = 1000))] + /// the request page index, starts from 1, max 1000 + #[validate(range(min = 1, max = 1000))] pub page_number: u64, + +} + +impl From for DatakeyPaginationQuery { + fn from(value: ListKeyQuery) -> Self { + Self { + page_size: value.page_size, + page_number: value.page_number, + name: value.name, + description: value.description, + key_type: value.key_type, + visibility: value.visibility + } + } } diff --git a/src/util/key.rs b/src/util/key.rs index 37f69140b4c9c63039fa5507f344a867dd06af94..57802b3352c8744da876dfdb42e85ee1df3ab33b 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -39,16 +39,15 @@ pub fn get_datakey_full_name(name: &str, email: &str, visibility: &Visibility) - } else { Err(Error::ParameterError("public key name should not contains ':'".to_string())) } - } else { - if names.len() <= 1 { - return Ok(format!("{}:{}", email, name)); - } else if names.len() > 2 { - return Err(Error::ParameterError("private key should in the format of {email}:{key_name}".to_string())) - } else if names[0] != email { - return Err(Error::ParameterError("private key email prefix not matched':'".to_string())) - } - return Ok(name.to_owned()) } + if names.len() <= 1 { + return Ok(format!("{}:{}", email, name)); + } else if names.len() > 2 { + return Err(Error::ParameterError("private key should in the format of {email}:{key_name}".to_string())) + } else if names[0] != email { + return Err(Error::ParameterError("private key email prefix not matched':'".to_string())) + } + Ok(name.to_owned()) } pub fn decode_hex_string_to_u8(value: &String) -> Vec {