From 015607322d1d4b592b5f0056aee1a655d94de3f8 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 18:50:43 +0100 Subject: [PATCH] refactor: CandidateDetails --- api/src/routes/candidate.rs | 7 +- core/src/candidate_details.rs | 173 +++++++++++++++++++++ core/src/database/mutation/candidate.rs | 4 +- core/src/lib.rs | 1 + core/src/services/candidate_service.rs | 192 ++---------------------- 5 files changed, 189 insertions(+), 188 deletions(-) create mode 100644 core/src/candidate_details.rs diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 61424be..60fee41 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; -use portfolio_core::services::candidate_service::{CandidateService, UserDetails}; +use portfolio_core::candidate_details::CandidateDetails; +use portfolio_core::services::candidate_service::{CandidateService}; use requests::LoginRequest; use rocket::http::{Cookie, CookieJar, Status}; use rocket::response::status::Custom; @@ -59,7 +60,7 @@ pub async fn whoami(session: CandidateAuth) -> Result> { #[post("/details", data = "
")] pub async fn fill_details( conn: Connection<'_, Db>, - details: Json, + details: Json, session: CandidateAuth, ) -> Result> { let db = conn.into_inner(); @@ -85,7 +86,7 @@ pub async fn get_details( conn: Connection<'_, Db>, password_form: Json, session: CandidateAuth, -) -> Result, Custom> { +) -> Result, Custom> { let db = conn.into_inner(); let candidate: entity::candidate::Model = session.into(); let password = password_form.password.clone(); diff --git a/core/src/candidate_details.rs b/core/src/candidate_details.rs new file mode 100644 index 0000000..72c78b7 --- /dev/null +++ b/core/src/candidate_details.rs @@ -0,0 +1,173 @@ +use entity::candidate; +use serde::{Serialize, Deserialize}; + +use crate::{error::ServiceError, crypto}; + +pub struct EncryptedString(String); + +impl EncryptedString { + pub async fn new(s: &str, recipients: &Vec<&str>) -> Result { + match crypto::encrypt_password_with_recipients(&s, &recipients).await{ + Ok(encrypted) => Ok(Self(encrypted)), + Err(_) => Err(ServiceError::CryptoEncryptFailed), + } + } + + pub async fn decrypt(&self, private_key: &String) -> Result { + match crypto::decrypt_password_with_private_key(&self.0, private_key).await { + Ok(decrypted) => Ok(decrypted), + Err(_) => Err(ServiceError::CryptoDecryptFailed), + } + } + + pub async fn to_string(self) -> String { + self.0 + } +} + +impl Into for EncryptedString { + fn into(self) -> String { + self.0 + } +} + +impl TryFrom> for EncryptedString { + type Error = ServiceError; + + fn try_from(s: Option) -> Result { + match s { + Some(s) => Ok(Self(s)), + None => Err(ServiceError::CandidateDetailsNotSet), + } + } +} + +pub(crate) struct EncryptedCandidateDetails { + pub name: EncryptedString, + pub surname: EncryptedString, + pub birthplace: EncryptedString, + // pub birthdate: NaiveDate, + pub address: EncryptedString, + pub telephone: EncryptedString, + pub citizenship: EncryptedString, + pub email: EncryptedString, + pub sex: EncryptedString, + pub study: EncryptedString, +} + +impl EncryptedCandidateDetails { + pub async fn new(form: CandidateDetails, recipients: Vec<&str>) -> EncryptedCandidateDetails { + let ( + Ok(name), + Ok(surname), + Ok(birthplace), + // Ok(enc_birthdate), + Ok(address), + Ok(telephone), + Ok(citizenship), + Ok(email), + Ok(sex), + Ok(study), + ) = tokio::join!( + EncryptedString::new(&form.name, &recipients), + EncryptedString::new(&form.surname, &recipients), + EncryptedString::new(&form.birthplace, &recipients), + // EncryptedString::new((&self.birthdate, &recipients), // TODO + EncryptedString::new(&form.address, &recipients), + EncryptedString::new(&form.telephone, &recipients), + EncryptedString::new(&form.citizenship, &recipients), + EncryptedString::new(&form.email, &recipients), + EncryptedString::new(&form.sex, &recipients), + EncryptedString::new(&form.study, &recipients), + ) else { + panic!("Failed to encrypt user details"); // TODO + }; + + EncryptedCandidateDetails { + name, + surname, + birthplace, + // birthdate: NaiveDate::from_ymd(2000, 1, 1), + address, + telephone, + citizenship, + email, + sex, + study, + } + } + + pub async fn decrypt(self, priv_key: String) -> Result { + let ( + Ok(name), + Ok(surname), + Ok(birthplace), + // Ok(enc_birthdate), + Ok(address), + Ok(telephone), + Ok(citizenship), + Ok(email), + Ok(sex), + Ok(study), + ) = tokio::join!( + self.name.decrypt(&priv_key), + self.surname.decrypt(&priv_key), + self.birthplace.decrypt(&priv_key), + // self.birthdate.decrypt(&priv_key), + self.address.decrypt(&priv_key), + self.telephone.decrypt(&priv_key), + self.citizenship.decrypt(&priv_key), + self.email.decrypt(&priv_key), + self.sex.decrypt(&priv_key), + self.study.decrypt(&priv_key), + ) else { + panic!("Failed to encrypt user details"); // TODO + }; + + Ok(CandidateDetails { + name, + surname, + birthplace, + // birthdate: NaiveDate::from_ymd(2000, 1, 1), + address, + telephone, + citizenship, + email, + sex, + study, + }) + } +} + +impl TryFrom for EncryptedCandidateDetails { + type Error = ServiceError; + + fn try_from(candidate: candidate::Model) -> Result { + Ok(EncryptedCandidateDetails { + name: EncryptedString::try_from(candidate.name)?, + surname: EncryptedString::try_from(candidate.surname)?, + birthplace: EncryptedString::try_from(candidate.birthplace)?, + // birthdate?, + address: EncryptedString::try_from(candidate.address)?, + telephone: EncryptedString::try_from(candidate.telephone)?, + citizenship: EncryptedString::try_from(candidate.citizenship)?, + email: EncryptedString::try_from(candidate.email)?, + sex: EncryptedString::try_from(candidate.sex)?, + study: EncryptedString::try_from(candidate.study)?, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CandidateDetails { + pub name: String, + pub surname: String, + pub birthplace: String, + // pub birthdate: NaiveDate, + pub address: String, + pub telephone: String, + pub citizenship: String, + pub email: String, + pub sex: String, + pub study: String, +} \ No newline at end of file diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index e2906cd..f1fe53e 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -1,4 +1,4 @@ -use crate::{Mutation, services::candidate_service::EncryptedUserDetails}; +use crate::{Mutation, candidate_details::EncryptedCandidateDetails}; use ::entity::candidate::{self}; use sea_orm::{*}; @@ -29,7 +29,7 @@ impl Mutation { pub async fn add_candidate_details( db: &DbConn, user: candidate::Model, - enc_details: EncryptedUserDetails, + enc_details: EncryptedCandidateDetails, ) -> Result { let mut user: candidate::ActiveModel = user.into(); user.name = Set(Some(enc_details.name.into())); diff --git a/core/src/lib.rs b/core/src/lib.rs index 1121e29..c910059 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,6 +3,7 @@ pub mod crypto; pub mod filetype; pub mod services; pub mod error; +pub mod candidate_details; pub use database::mutation::*; pub use database::query::*; diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 5228500..1e8cec8 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,190 +1,16 @@ use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; -use serde::{Deserialize, Serialize}; use crate::{ crypto::{self, hash_password}, error::ServiceError, - Mutation, Query, + Mutation, Query, candidate_details::{CandidateDetails, EncryptedCandidateDetails}, }; use super::session_service::{AdminUser, SessionService}; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; -pub struct EncryptedString(String); - -impl EncryptedString { - pub async fn new(s: &str, recipients: &Vec<&str>) -> Result { - match crypto::encrypt_password_with_recipients(&s, &recipients).await{ - Ok(encrypted) => Ok(Self(encrypted)), - Err(_) => Err(ServiceError::CryptoEncryptFailed), - } - } - - pub async fn decrypt(&self, private_key: &String) -> Result { - match crypto::decrypt_password_with_private_key(&self.0, private_key).await { - Ok(decrypted) => Ok(decrypted), - Err(_) => Err(ServiceError::CryptoDecryptFailed), - } - } - - pub async fn to_string(self) -> String { - self.0 - } -} - -impl Into for EncryptedString { - fn into(self) -> String { - self.0 - } -} - -impl TryFrom> for EncryptedString { - type Error = ServiceError; - - fn try_from(s: Option) -> Result { - match s { - Some(s) => Ok(Self(s)), - None => Err(ServiceError::CandidateDetailsNotSet), - } - } -} - -pub(crate) struct EncryptedUserDetails { - pub name: EncryptedString, - pub surname: EncryptedString, - pub birthplace: EncryptedString, - // pub birthdate: NaiveDate, - pub address: EncryptedString, - pub telephone: EncryptedString, - pub citizenship: EncryptedString, - pub email: EncryptedString, - pub sex: EncryptedString, - pub study: EncryptedString, -} - -impl EncryptedUserDetails { - pub async fn new(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails { - let ( - Ok(name), - Ok(surname), - Ok(birthplace), - // Ok(enc_birthdate), - Ok(address), - Ok(telephone), - Ok(citizenship), - Ok(email), - Ok(sex), - Ok(study), - ) = tokio::join!( - EncryptedString::new(&form.name, &recipients), - EncryptedString::new(&form.surname, &recipients), - EncryptedString::new(&form.birthplace, &recipients), - // EncryptedString::new((&self.birthdate, &recipients), // TODO - EncryptedString::new(&form.address, &recipients), - EncryptedString::new(&form.telephone, &recipients), - EncryptedString::new(&form.citizenship, &recipients), - EncryptedString::new(&form.email, &recipients), - EncryptedString::new(&form.sex, &recipients), - EncryptedString::new(&form.study, &recipients), - ) else { - panic!("Failed to encrypt user details"); // TODO - }; - - EncryptedUserDetails { - name, - surname, - birthplace, - // birthdate: NaiveDate::from_ymd(2000, 1, 1), - address, - telephone, - citizenship, - email, - sex, - study, - } - } - - pub async fn decrypt(self, priv_key: String) -> Result { - let ( - Ok(name), - Ok(surname), - Ok(birthplace), - // Ok(enc_birthdate), - Ok(address), - Ok(telephone), - Ok(citizenship), - Ok(email), - Ok(sex), - Ok(study), - ) = tokio::join!( - self.name.decrypt(&priv_key), - self.surname.decrypt(&priv_key), - self.birthplace.decrypt(&priv_key), - // self.birthdate.decrypt(&priv_key), - self.address.decrypt(&priv_key), - self.telephone.decrypt(&priv_key), - self.citizenship.decrypt(&priv_key), - self.email.decrypt(&priv_key), - self.sex.decrypt(&priv_key), - self.study.decrypt(&priv_key), - ) else { - panic!("Failed to encrypt user details"); // TODO - }; - - Ok(UserDetails { - name, - surname, - birthplace, - // birthdate: NaiveDate::from_ymd(2000, 1, 1), - address, - telephone, - citizenship, - email, - sex, - study, - }) - } -} - -impl TryFrom for EncryptedUserDetails { - type Error = ServiceError; - - fn try_from(candidate: candidate::Model) -> Result { - if !CandidateService::is_set_up(&candidate) { - return Err(ServiceError::CandidateDetailsNotSet); - } - - Ok(EncryptedUserDetails { - name: EncryptedString::try_from(candidate.name)?, - surname: EncryptedString::try_from(candidate.surname)?, - birthplace: EncryptedString::try_from(candidate.birthplace)?, - // birthdate?, - address: EncryptedString::try_from(candidate.address)?, - telephone: EncryptedString::try_from(candidate.telephone)?, - citizenship: EncryptedString::try_from(candidate.citizenship)?, - email: EncryptedString::try_from(candidate.email)?, - sex: EncryptedString::try_from(candidate.sex)?, - study: EncryptedString::try_from(candidate.study)?, - }) - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct UserDetails { - pub name: String, - pub surname: String, - pub birthplace: String, - // pub birthdate: NaiveDate, - pub address: String, - pub telephone: String, - pub citizenship: String, - pub email: String, - pub sex: String, - pub study: String, -} - pub struct CandidateService; impl CandidateService { @@ -245,7 +71,7 @@ impl CandidateService { pub async fn add_user_details( db: &DbConn, user: candidate::Model, - form: UserDetails, + form: CandidateDetails, ) -> Result { let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { return Err(ServiceError::DbError); @@ -258,7 +84,7 @@ impl CandidateService { recipients.append(&mut admin_public_keys_refrence); - let enc_details = EncryptedUserDetails::new(form, recipients).await; + let enc_details = EncryptedCandidateDetails::new(form, recipients).await; Mutation::add_candidate_details(db, user, enc_details) .await @@ -269,7 +95,7 @@ impl CandidateService { db: &DbConn, candidate_id: i32, password: String, - ) -> Result { + ) -> Result { let candidate = match Query::find_candidate_by_id(db, candidate_id).await { Ok(candidate) => candidate.unwrap(), Err(_) => return Err(ServiceError::DbError), // TODO: logging @@ -288,7 +114,7 @@ impl CandidateService { .await .ok() .unwrap(); - let enc_details = EncryptedUserDetails::try_from(candidate)?; + let enc_details = EncryptedCandidateDetails::try_from(candidate)?; enc_details.decrypt(dec_priv_key).await } @@ -395,10 +221,10 @@ mod tests { use crate::{ crypto, - services::candidate_service::{CandidateService, UserDetails}, + services::candidate_service::{CandidateService, CandidateDetails}, }; - use super::EncryptedUserDetails; + use super::EncryptedCandidateDetails; #[tokio::test] async fn test_application_id_validation() { @@ -473,7 +299,7 @@ mod tests { .ok() .unwrap(); - let form = UserDetails { + let form = CandidateDetails { name: "test".to_string(), surname: "a".to_string(), birthplace: "b".to_string(), @@ -507,7 +333,7 @@ mod tests { let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await .unwrap(); - let dec_candidate = EncryptedUserDetails::try_from(enc_candidate) + let dec_candidate = EncryptedCandidateDetails::try_from(enc_candidate) .unwrap() .decrypt(dec_priv_key) .await