From e3a37dd59b9f099af971dceb9eeeaf7e41e13216 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 18:44:42 +0100 Subject: [PATCH 01/12] refactor: encrypted user details --- core/src/database/mutation/candidate.rs | 18 +-- core/src/services/candidate_service.rs | 190 ++++++++++++------------ 2 files changed, 105 insertions(+), 103 deletions(-) diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index dd7ab01..e2906cd 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -32,15 +32,15 @@ impl Mutation { enc_details: EncryptedUserDetails, ) -> Result { let mut user: candidate::ActiveModel = user.into(); - user.name = Set(Some(enc_details.name)); - user.surname = Set(Some(enc_details.surname)); - user.birthplace = Set(Some(enc_details.birthplace)); - user.address = Set(Some(enc_details.address)); - user.telephone = Set(Some(enc_details.telephone)); - user.citizenship = Set(Some(enc_details.citizenship)); - user.email = Set(Some(enc_details.email)); - user.sex = Set(Some(enc_details.sex)); - user.study = Set(Some(enc_details.study)); + user.name = Set(Some(enc_details.name.into())); + user.surname = Set(Some(enc_details.surname.into())); + user.birthplace = Set(Some(enc_details.birthplace.into())); + user.address = Set(Some(enc_details.address.into())); + user.telephone = Set(Some(enc_details.telephone.into())); + user.citizenship = Set(Some(enc_details.citizenship.into())); + user.email = Set(Some(enc_details.email.into())); + user.sex = Set(Some(enc_details.sex.into())); + user.study = Set(Some(enc_details.study.into())); user.updated_at = Set(chrono::offset::Local::now().naive_local()); diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index c63f37d..5228500 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -12,21 +12,60 @@ 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: String, - pub surname: String, - pub birthplace: String, + pub name: EncryptedString, + pub surname: EncryptedString, + pub birthplace: EncryptedString, // pub birthdate: NaiveDate, - pub address: String, - pub telephone: String, - pub citizenship: String, - pub email: String, - pub sex: String, - pub study: String, + pub address: EncryptedString, + pub telephone: EncryptedString, + pub citizenship: EncryptedString, + pub email: EncryptedString, + pub sex: EncryptedString, + pub study: EncryptedString, } impl EncryptedUserDetails { - pub async fn encrypt_form(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails { + pub async fn new(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails { let ( Ok(name), Ok(surname), @@ -39,16 +78,16 @@ impl EncryptedUserDetails { Ok(sex), Ok(study), ) = tokio::join!( - crypto::encrypt_password_with_recipients(&form.name, &recipients), - crypto::encrypt_password_with_recipients(&form.surname, &recipients), - crypto::encrypt_password_with_recipients(&form.birthplace, &recipients), - // crypto::encrypt_password_with_recipients(&self.birthdate, &recipients), // TODO - crypto::encrypt_password_with_recipients(&form.address, &recipients), - crypto::encrypt_password_with_recipients(&form.telephone, &recipients), - crypto::encrypt_password_with_recipients(&form.citizenship, &recipients), - crypto::encrypt_password_with_recipients(&form.email, &recipients), - crypto::encrypt_password_with_recipients(&form.sex, &recipients), - crypto::encrypt_password_with_recipients(&form.study, &recipients), + 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 }; @@ -67,67 +106,6 @@ impl EncryptedUserDetails { } } - fn extract_enc_candidate_details( - candidate: candidate::Model, - ) -> Result { - let ( // TODO: simplify?? - Some(name), - Some(surname), - Some(birthplace), - // Some(birthdate), - Some(address), - Some(telephone), - Some(citizenship), - Some(email), - Some(sex), - Some(study), - ) = ( - candidate.name, - candidate.surname, - candidate.birthplace, - // candidate.birthdate, - candidate.address, - candidate.telephone, - candidate.citizenship, - candidate.email, - candidate.sex, - candidate.study, - ) else { - return Err(ServiceError::CandidateDetailsNotSet); - }; - - Ok(UserDetails { - name, - surname, - birthplace, - // birthdate, - address, - telephone, - citizenship, - email, - sex, - study, - }) - } - - pub fn from_model(candidate: candidate::Model) -> Result { - let Ok(details) = Self::extract_enc_candidate_details(candidate) else { - return Err(ServiceError::CandidateDetailsNotSet); - }; - Ok(EncryptedUserDetails { - name: details.name, - surname: details.surname, - birthplace: details.birthplace, - // birthdate, - address: details.address, - telephone: details.telephone, - citizenship: details.citizenship, - email: details.email, - sex: details.sex, - study: details.study, - }) - } - pub async fn decrypt(self, priv_key: String) -> Result { let ( Ok(name), @@ -141,15 +119,16 @@ impl EncryptedUserDetails { Ok(sex), Ok(study), ) = tokio::join!( - crypto::decrypt_password_with_private_key(&self.name, &priv_key), - crypto::decrypt_password_with_private_key(&self.surname, &priv_key), - crypto::decrypt_password_with_private_key(&self.birthplace, &priv_key), - crypto::decrypt_password_with_private_key(&self.address, &priv_key), - crypto::decrypt_password_with_private_key(&self.telephone, &priv_key), - crypto::decrypt_password_with_private_key(&self.citizenship, &priv_key), - crypto::decrypt_password_with_private_key(&self.email, &priv_key), - crypto::decrypt_password_with_private_key(&self.sex, &priv_key), - crypto::decrypt_password_with_private_key(&self.study, &priv_key), + 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 }; @@ -169,6 +148,29 @@ impl EncryptedUserDetails { } } +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, @@ -256,7 +258,7 @@ impl CandidateService { recipients.append(&mut admin_public_keys_refrence); - let enc_details = EncryptedUserDetails::encrypt_form(form, recipients).await; + let enc_details = EncryptedUserDetails::new(form, recipients).await; Mutation::add_candidate_details(db, user, enc_details) .await @@ -286,12 +288,12 @@ impl CandidateService { .await .ok() .unwrap(); - let enc_details = EncryptedUserDetails::from_model(candidate)?; + let enc_details = EncryptedUserDetails::try_from(candidate)?; enc_details.decrypt(dec_priv_key).await } - pub async fn is_set_up(candidate: &candidate::Model) -> bool { + pub fn is_set_up(candidate: &candidate::Model) -> bool { candidate.name.is_some() && candidate.surname.is_some() && candidate.birthplace.is_some() && @@ -505,7 +507,7 @@ mod tests { let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await .unwrap(); - let dec_candidate = EncryptedUserDetails::from_model(enc_candidate) + let dec_candidate = EncryptedUserDetails::try_from(enc_candidate) .unwrap() .decrypt(dec_priv_key) .await From 015607322d1d4b592b5f0056aee1a655d94de3f8 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 18:50:43 +0100 Subject: [PATCH 02/12] 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 From 5b70d8df4f08b336674e32b401a3c76585a54f0f Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 18:53:01 +0100 Subject: [PATCH 03/12] refactor: candidate variables --- api/src/routes/candidate.rs | 2 +- core/src/services/admin_service.rs | 2 +- core/src/services/candidate_service.rs | 18 +++++++++--------- core/src/services/session_service.rs | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 60fee41..671c1a7 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -67,7 +67,7 @@ pub async fn fill_details( let form = details.into_inner(); let candidate: entity::candidate::Model = session.into(); - let candidate = CandidateService::add_user_details(db, candidate, form).await; + let candidate = CandidateService::add_candidate_details(db, candidate, form).await; if candidate.is_err() { // TODO cleanup diff --git a/core/src/services/admin_service.rs b/core/src/services/admin_service.rs index f6c27a8..d0d9cdd 100644 --- a/core/src/services/admin_service.rs +++ b/core/src/services/admin_service.rs @@ -55,7 +55,7 @@ impl AdminService { match SessionService::auth_user_session(db, session_uuid).await { Ok(user) => match user { AdminUser::Admin(admin) => Ok(admin), - AdminUser::User(_) => Err(ServiceError::DbError), + AdminUser::Candidate(_) => Err(ServiceError::DbError), }, Err(e) => Err(e), } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 1e8cec8..41c25c1 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -68,9 +68,9 @@ impl CandidateService { .map_err(|_| ServiceError::DbError) } - pub async fn add_user_details( + pub async fn add_candidate_details( db: &DbConn, - user: candidate::Model, + candidate: candidate::Model, form: CandidateDetails, ) -> Result { let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { @@ -80,13 +80,13 @@ impl CandidateService { let mut admin_public_keys_refrence: Vec<&str> = admin_public_keys.iter().map(|s| &**s).collect(); - let mut recipients = vec![&*user.public_key]; + let mut recipients = vec![&*candidate.public_key]; recipients.append(&mut admin_public_keys_refrence); let enc_details = EncryptedCandidateDetails::new(form, recipients).await; - Mutation::add_candidate_details(db, user, enc_details) + Mutation::add_candidate_details(db, candidate, enc_details) .await .map_err(|_| ServiceError::DbError) } @@ -178,15 +178,15 @@ impl CandidateService { pub async fn login( db: &DbConn, - user_id: i32, + candidate_id: i32, password: String, ip_addr: String, ) -> Result<(String, String), ServiceError> { let session_id = - SessionService::new_session(db, Some(user_id), None, password.clone(), ip_addr).await; + SessionService::new_session(db, Some(candidate_id), None, password.clone(), ip_addr).await; match session_id { Ok(session_id) => { - let private_key = Self::decrypt_private_key(db, user_id, password).await?; + let private_key = Self::decrypt_private_key(db, candidate_id, password).await?; Ok((session_id, private_key)) } Err(e) => Err(e), @@ -196,7 +196,7 @@ impl CandidateService { pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result { match SessionService::auth_user_session(db, session_uuid).await { Ok(user) => match user { - AdminUser::User(candidate) => Ok(candidate), + AdminUser::Candidate(candidate) => Ok(candidate), AdminUser::Admin(_) => Err(ServiceError::DbError), }, Err(e) => Err(e), @@ -311,7 +311,7 @@ mod tests { sex: "test".to_string(), study: "test".to_string(), }; - CandidateService::add_user_details(&db, candidate, form) + CandidateService::add_candidate_details(&db, candidate, form) .await .ok() .unwrap() diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index f827679..2474b29 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -11,7 +11,7 @@ use crate::{ pub enum AdminUser { Admin(entity::admin::Model), - User(entity::candidate::Model), + Candidate(entity::candidate::Model), } pub(in crate::services) struct SessionService; @@ -147,7 +147,7 @@ impl SessionService { if candidate.is_ok() { if let Some(candidate) = candidate.unwrap() { - return Ok(AdminUser::User(candidate)); + return Ok(AdminUser::Candidate(candidate)); } } From 22ea58d521ba9220e531ecbeff8cdc72fe8b2e28 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 19:09:00 +0100 Subject: [PATCH 04/12] refactor: candidate details enc, dec methods --- core/src/candidate_details.rs | 80 +++++++++----------------- core/src/services/candidate_service.rs | 2 +- 2 files changed, 28 insertions(+), 54 deletions(-) diff --git a/core/src/candidate_details.rs b/core/src/candidate_details.rs index 72c78b7..020abb0 100644 --- a/core/src/candidate_details.rs +++ b/core/src/candidate_details.rs @@ -56,60 +56,36 @@ pub(crate) struct EncryptedCandidateDetails { } 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!( + pub async fn new(form: CandidateDetails, recipients: Vec<&str>) -> Result { + let d = tokio::try_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.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, + Ok(EncryptedCandidateDetails { + name: d.0, + surname: d.1, + birthplace: d.2, // birthdate: NaiveDate::from_ymd(2000, 1, 1), - address, - telephone, - citizenship, - email, - sex, - study, - } + address: d.4, + telephone: d.5, + citizenship: d.6, + email: d.7, + sex: d.8, + study: d.9, + }) } 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!( + let d = tokio::try_join!( self.name.decrypt(&priv_key), self.surname.decrypt(&priv_key), self.birthplace.decrypt(&priv_key), @@ -120,21 +96,19 @@ impl EncryptedCandidateDetails { 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, + name: d.0, + surname: d.1, + birthplace: d.2, + // birthdate: NaiveDate::from_ymd(2000, 1, 1), // TODO + address: d.3, + telephone: d.4, + citizenship: d.5, + email: d.6, + sex: d.7, + study: d.8, }) } } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 41c25c1..188e6fb 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -84,7 +84,7 @@ impl CandidateService { recipients.append(&mut admin_public_keys_refrence); - let enc_details = EncryptedCandidateDetails::new(form, recipients).await; + let enc_details = EncryptedCandidateDetails::new(form, recipients).await?; Mutation::add_candidate_details(db, candidate, enc_details) .await From 3dff252a0a77adac5c749495edf6dbb97c86fda6 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 19:44:51 +0100 Subject: [PATCH 05/12] feat: birthdate in candidate details --- core/src/candidate_details.rs | 43 +++++++++++++------ core/src/database/mutation/candidate.rs | 3 +- core/src/services/candidate_service.rs | 4 +- entity/src/candidate.rs | 2 +- .../src/m20221024_121621_create_candidate.rs | 2 +- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/core/src/candidate_details.rs b/core/src/candidate_details.rs index 020abb0..027cf5f 100644 --- a/core/src/candidate_details.rs +++ b/core/src/candidate_details.rs @@ -1,8 +1,11 @@ +use chrono::{NaiveDate}; use entity::candidate; use serde::{Serialize, Deserialize}; use crate::{error::ServiceError, crypto}; +pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; + pub struct EncryptedString(String); impl EncryptedString { @@ -20,7 +23,7 @@ impl EncryptedString { } } - pub async fn to_string(self) -> String { + pub fn to_string(self) -> String { self.0 } } @@ -42,11 +45,22 @@ impl TryFrom> for EncryptedString { } } +impl TryFrom> for EncryptedString { // TODO: take a look at this + type Error = ServiceError; + + fn try_from(d: Option) -> Result { + match d { + Some(d) => Ok(Self(d.to_string())), + None => Err(ServiceError::CandidateDetailsNotSet), + } + } +} + pub(crate) struct EncryptedCandidateDetails { pub name: EncryptedString, pub surname: EncryptedString, pub birthplace: EncryptedString, - // pub birthdate: NaiveDate, + pub birthdate: EncryptedString, pub address: EncryptedString, pub telephone: EncryptedString, pub citizenship: EncryptedString, @@ -57,11 +71,12 @@ pub(crate) struct EncryptedCandidateDetails { impl EncryptedCandidateDetails { pub async fn new(form: CandidateDetails, recipients: Vec<&str>) -> Result { + let birthdate_str = form.birthdate.format(NAIVE_DATE_FMT).to_string(); let d = tokio::try_join!( EncryptedString::new(&form.name, &recipients), EncryptedString::new(&form.surname, &recipients), EncryptedString::new(&form.birthplace, &recipients), - EncryptedString::new("&form.birthdate", &recipients), // TODO + EncryptedString::new(&birthdate_str, &recipients), // TODO EncryptedString::new(&form.address, &recipients), EncryptedString::new(&form.telephone, &recipients), EncryptedString::new(&form.citizenship, &recipients), @@ -74,7 +89,7 @@ impl EncryptedCandidateDetails { name: d.0, surname: d.1, birthplace: d.2, - // birthdate: NaiveDate::from_ymd(2000, 1, 1), + birthdate: d.3, address: d.4, telephone: d.5, citizenship: d.6, @@ -89,7 +104,7 @@ impl EncryptedCandidateDetails { self.name.decrypt(&priv_key), self.surname.decrypt(&priv_key), self.birthplace.decrypt(&priv_key), - // self.birthdate.decrypt(&priv_key), + self.birthdate.decrypt(&priv_key), self.address.decrypt(&priv_key), self.telephone.decrypt(&priv_key), self.citizenship.decrypt(&priv_key), @@ -102,13 +117,13 @@ impl EncryptedCandidateDetails { name: d.0, surname: d.1, birthplace: d.2, - // birthdate: NaiveDate::from_ymd(2000, 1, 1), // TODO - address: d.3, - telephone: d.4, - citizenship: d.5, - email: d.6, - sex: d.7, - study: d.8, + birthdate: NaiveDate::parse_from_str(&d.3, NAIVE_DATE_FMT).unwrap(), // TODO + address: d.4, + telephone: d.5, + citizenship: d.6, + email: d.7, + sex: d.8, + study: d.9, }) } } @@ -121,7 +136,7 @@ impl TryFrom for EncryptedCandidateDetails { name: EncryptedString::try_from(candidate.name)?, surname: EncryptedString::try_from(candidate.surname)?, birthplace: EncryptedString::try_from(candidate.birthplace)?, - // birthdate?, + birthdate: EncryptedString::try_from(candidate.birthdate)?, address: EncryptedString::try_from(candidate.address)?, telephone: EncryptedString::try_from(candidate.telephone)?, citizenship: EncryptedString::try_from(candidate.citizenship)?, @@ -137,7 +152,7 @@ pub struct CandidateDetails { pub name: String, pub surname: String, pub birthplace: String, - // pub birthdate: NaiveDate, + pub birthdate: NaiveDate, // TODO: User NaiveDate or String? pub address: String, pub telephone: String, pub citizenship: String, diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index f1fe53e..9b191b0 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -1,4 +1,4 @@ -use crate::{Mutation, candidate_details::EncryptedCandidateDetails}; +use crate::{Mutation, candidate_details::{EncryptedCandidateDetails}}; use ::entity::candidate::{self}; use sea_orm::{*}; @@ -35,6 +35,7 @@ impl Mutation { user.name = Set(Some(enc_details.name.into())); user.surname = Set(Some(enc_details.surname.into())); user.birthplace = Set(Some(enc_details.birthplace.into())); + user.birthdate = Set(Some(enc_details.birthdate.into())); user.address = Set(Some(enc_details.address.into())); user.telephone = Set(Some(enc_details.telephone.into())); user.citizenship = Set(Some(enc_details.citizenship.into())); diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 188e6fb..3cd7651 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -293,6 +293,8 @@ mod tests { #[cfg(test)] async fn put_user_data(db: &DbConn) -> Model { + use chrono::NaiveDate; + let plain_text_password = "test".to_string(); let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) .await @@ -303,7 +305,7 @@ mod tests { name: "test".to_string(), surname: "a".to_string(), birthplace: "b".to_string(), - // birthdate: NaiveDate::from_ymd(1999, 1, 1), + birthdate: NaiveDate::from_ymd(1999, 1, 1), address: "test".to_string(), telephone: "test".to_string(), citizenship: "test".to_string(), diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index bf738f9..002093d 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -10,7 +10,7 @@ pub struct Model { pub surname: Option, pub birth_surname: Option, pub birthplace: Option, - pub birthdate: Option, + pub birthdate: Option, pub address: Option, pub telephone: Option, pub citizenship: Option, diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index 2b7bc26..66d6466 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -23,7 +23,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Candidate::Surname).string()) .col(ColumnDef::new(Candidate::BirthSurname).string()) .col(ColumnDef::new(Candidate::Birthplace).string()) - .col(ColumnDef::new(Candidate::Birthdate).date()) + .col(ColumnDef::new(Candidate::Birthdate).string()) .col(ColumnDef::new(Candidate::Address).string()) .col(ColumnDef::new(Candidate::Telephone).string()) .col(ColumnDef::new(Candidate::Citizenship).string()) From 9e1ba16ef69cb4e0eb20b44149d0cee5e3df43be Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 23:29:09 +0100 Subject: [PATCH 06/12] feat: parent details --- api/src/routes/admin.rs | 14 +--- core/src/candidate_details.rs | 71 +++++++++++++++---- core/src/database/mutation/parent.rs | 19 +++-- core/src/database/query/mod.rs | 3 +- core/src/database/query/parent.rs | 17 +++++ core/src/error.rs | 6 +- core/src/services/admin_service.rs | 2 +- core/src/services/application_service.rs | 5 ++ core/src/services/candidate_service.rs | 60 +++++++++------- core/src/services/mod.rs | 4 +- core/src/services/parent_service.rs | 36 ++++++++++ core/src/services/session_service.rs | 19 ++--- entity/src/candidate.rs | 12 +--- entity/src/parent.rs | 15 +--- migration/src/lib.rs | 2 - ...21030_133428_parent_create_candidate_fk.rs | 26 ------- 16 files changed, 184 insertions(+), 127 deletions(-) create mode 100644 core/src/database/query/parent.rs create mode 100644 core/src/services/application_service.rs create mode 100644 core/src/services/parent_service.rs delete mode 100644 migration/src/m20221030_133428_parent_create_candidate_fk.rs diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 68a6062..9d110df 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -75,22 +75,14 @@ pub async fn create_candidate( let plain_text_password = random_8_char_string(); - let candidate = CandidateService::create( + CandidateService::create( db, form.application_id, &plain_text_password, form.personal_id_number, ) - .await; - - if candidate.is_err() { - // TODO cleanup - let e = candidate.err().unwrap(); - return Err(Custom( - Status::from_code(e.code()).unwrap_or_default(), - e.message(), - )); - } + .await + .map_err(|e| Custom(Status::InternalServerError, e.to_string()))?; Ok(plain_text_password) } diff --git a/core/src/candidate_details.rs b/core/src/candidate_details.rs index 027cf5f..7b20ab2 100644 --- a/core/src/candidate_details.rs +++ b/core/src/candidate_details.rs @@ -1,11 +1,12 @@ use chrono::{NaiveDate}; -use entity::candidate; +use entity::{candidate, parent}; use serde::{Serialize, Deserialize}; use crate::{error::ServiceError, crypto}; pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; +#[derive(Clone)] pub struct EncryptedString(String); impl EncryptedString { @@ -56,7 +57,9 @@ impl TryFrom> for EncryptedString { // TODO: take a look at th } } -pub(crate) struct EncryptedCandidateDetails { +#[derive(Clone)] +pub struct EncryptedCandidateDetails { + // Candidate pub name: EncryptedString, pub surname: EncryptedString, pub birthplace: EncryptedString, @@ -67,6 +70,12 @@ pub(crate) struct EncryptedCandidateDetails { pub email: EncryptedString, pub sex: EncryptedString, pub study: EncryptedString, + + // Parent + pub parent_name: EncryptedString, + pub parent_surname: EncryptedString, + pub parent_telephone: EncryptedString, + pub parent_email: EncryptedString, } impl EncryptedCandidateDetails { @@ -76,13 +85,18 @@ impl EncryptedCandidateDetails { EncryptedString::new(&form.name, &recipients), EncryptedString::new(&form.surname, &recipients), EncryptedString::new(&form.birthplace, &recipients), - EncryptedString::new(&birthdate_str, &recipients), // TODO + EncryptedString::new(&birthdate_str, &recipients), 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), + + EncryptedString::new(&form.parent_name, &recipients), + EncryptedString::new(&form.parent_surname, &recipients), + EncryptedString::new(&form.parent_telephone, &recipients), + EncryptedString::new(&form.parent_email, &recipients), )?; Ok(EncryptedCandidateDetails { @@ -96,21 +110,31 @@ impl EncryptedCandidateDetails { email: d.7, sex: d.8, study: d.9, + + parent_name: d.10, + parent_surname: d.11, + parent_telephone: d.12, + parent_email: d.13, }) } pub async fn decrypt(self, priv_key: String) -> Result { let d = tokio::try_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), + self.name.decrypt(&priv_key), // 0 + self.surname.decrypt(&priv_key), // 1 + self.birthplace.decrypt(&priv_key), // 2 + self.birthdate.decrypt(&priv_key), // 3 + self.address.decrypt(&priv_key), // 4 + self.telephone.decrypt(&priv_key), // 5 + self.citizenship.decrypt(&priv_key), // 6 + self.email.decrypt(&priv_key), // 7 + self.sex.decrypt(&priv_key), // 8 + self.study.decrypt(&priv_key), // 9 + + self.parent_name.decrypt(&priv_key), + self.parent_surname.decrypt(&priv_key), + self.parent_telephone.decrypt(&priv_key), + self.parent_email.decrypt(&priv_key), )?; Ok(CandidateDetails { @@ -124,14 +148,19 @@ impl EncryptedCandidateDetails { email: d.7, sex: d.8, study: d.9, + + parent_name: d.10, + parent_surname: d.11, + parent_telephone: d.12, + parent_email: d.13, }) } } -impl TryFrom for EncryptedCandidateDetails { +impl TryFrom<(candidate::Model, parent::Model)> for EncryptedCandidateDetails { type Error = ServiceError; - fn try_from(candidate: candidate::Model) -> Result { + fn try_from((candidate, parent): (candidate::Model, parent::Model)) -> Result { Ok(EncryptedCandidateDetails { name: EncryptedString::try_from(candidate.name)?, surname: EncryptedString::try_from(candidate.surname)?, @@ -143,12 +172,18 @@ impl TryFrom for EncryptedCandidateDetails { email: EncryptedString::try_from(candidate.email)?, sex: EncryptedString::try_from(candidate.sex)?, study: EncryptedString::try_from(candidate.study)?, + + parent_name: EncryptedString::try_from(parent.name)?, + parent_surname: EncryptedString::try_from(parent.surname)?, + parent_telephone: EncryptedString::try_from(parent.telephone)?, + parent_email: EncryptedString::try_from(parent.email)?, }) } } #[derive(Debug, Serialize, Deserialize)] pub struct CandidateDetails { + // Candidate pub name: String, pub surname: String, pub birthplace: String, @@ -159,4 +194,10 @@ pub struct CandidateDetails { pub email: String, pub sex: String, pub study: String, + + // Parent + pub parent_name: String, + pub parent_surname: String, + pub parent_telephone: String, + pub parent_email: String, } \ No newline at end of file diff --git a/core/src/database/mutation/parent.rs b/core/src/database/mutation/parent.rs index 80c7a7e..2855a29 100644 --- a/core/src/database/mutation/parent.rs +++ b/core/src/database/mutation/parent.rs @@ -1,4 +1,4 @@ -use crate::Mutation; +use crate::{Mutation, candidate_details::EncryptedCandidateDetails}; use ::entity::parent::{self, Model}; use sea_orm::*; @@ -17,17 +17,14 @@ impl Mutation { pub async fn add_parent_details( db: &DbConn, - user: Model, - name: String, - surname: String, - telephone: String, - email: String, + parent: Model, + enc_details: EncryptedCandidateDetails, // TODO: use seperate struct?? ) -> Result { - let mut user: parent::ActiveModel = user.into(); - user.name = Set(Some(name)); - user.surname = Set(Some(surname)); - user.telephone = Set(Some(telephone)); - user.email = Set(Some(email)); + let mut user: parent::ActiveModel = parent.into(); + user.name = Set(Some(enc_details.parent_name.into())); + user.surname = Set(Some(enc_details.parent_surname.into())); + user.telephone = Set(Some(enc_details.parent_telephone.into())); + user.email = Set(Some(enc_details.parent_email.into())); user.updated_at = Set(chrono::offset::Local::now().naive_local()); diff --git a/core/src/database/query/mod.rs b/core/src/database/query/mod.rs index 73362b8..90575e6 100644 --- a/core/src/database/query/mod.rs +++ b/core/src/database/query/mod.rs @@ -2,4 +2,5 @@ pub struct Query; pub mod candidate; pub mod admin; -pub mod session; \ No newline at end of file +pub mod session; +pub mod parent; \ No newline at end of file diff --git a/core/src/database/query/parent.rs b/core/src/database/query/parent.rs new file mode 100644 index 0000000..7fc0429 --- /dev/null +++ b/core/src/database/query/parent.rs @@ -0,0 +1,17 @@ + +use entity::parent::Model; +use entity::parent::Entity; +use sea_orm::{DbConn, DbErr}; +use sea_orm::EntityTrait; + +use crate::Query; + +impl Query { + pub async fn find_parent_by_id( + db: &DbConn, + application_id: i32, + ) -> Result, DbErr> { + + Entity::find_by_id(application_id).one(db).await + } +} \ No newline at end of file diff --git a/core/src/error.rs b/core/src/error.rs index 428b0f9..0627fdc 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -5,7 +5,8 @@ pub enum ServiceError { ExpiredSession, JwtError, UserAlreadyExists, - UserNotFound, + CandidateNotFound, + ParentNotFound, DbError, UserNotFoundByJwtId, UserNotFoundBySessionId, @@ -24,7 +25,8 @@ impl ServiceError { ServiceError::ExpiredSession => (401, "Session expired, please login again".to_string()), ServiceError::JwtError => (500, "Error while encoding JWT".to_string()), ServiceError::UserAlreadyExists => (409, "User already exists".to_string()), - ServiceError::UserNotFound => (404, "User not found".to_string()), + ServiceError::CandidateNotFound => (404, "User not found".to_string()), + ServiceError::ParentNotFound => (500, "Parent not found".to_string()), ServiceError::DbError => (500, "Database error".to_string()), ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()), ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()), diff --git a/core/src/services/admin_service.rs b/core/src/services/admin_service.rs index d0d9cdd..12dd925 100644 --- a/core/src/services/admin_service.rs +++ b/core/src/services/admin_service.rs @@ -20,7 +20,7 @@ impl AdminService { }; let Some(admin) = admin else { - return Err(ServiceError::UserNotFound); + return Err(ServiceError::CandidateNotFound); }; let private_key_encrypted = admin.private_key; diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs new file mode 100644 index 0000000..664bae6 --- /dev/null +++ b/core/src/services/application_service.rs @@ -0,0 +1,5 @@ +/* pub struct ApplicationService; + +impl ApplicationService { + pub fn create +} */ \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 3cd7651..d0ed56a 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,4 +1,4 @@ -use entity::candidate; +use entity::{candidate}; use sea_orm::{prelude::Uuid, DbConn}; use crate::{ @@ -7,7 +7,7 @@ use crate::{ Mutation, Query, candidate_details::{CandidateDetails, EncryptedCandidateDetails}, }; -use super::session_service::{AdminUser, SessionService}; +use super::{session_service::{AdminUser, SessionService}, parent_service::ParentService}; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; @@ -52,9 +52,9 @@ impl CandidateService { let Ok(hashed_personal_id_number) = hash_password(personal_id_number).await else { return Err(ServiceError::CryptoHashFailed); }; - /* let encrypted_personal_id_number = crypto::encrypt_password_with_recipients( - &personal_id_number, &vec![&pubkey] - ).await.unwrap(); */ + + ParentService::create_parent(db, application_id) + .await?; Mutation::create_candidate( db, @@ -64,8 +64,8 @@ impl CandidateService { pubkey, encrypted_priv_key, ) - .await - .map_err(|_| ServiceError::DbError) + .await + .map_err(|_| ServiceError::DbError) } pub async fn add_candidate_details( @@ -85,21 +85,23 @@ impl CandidateService { recipients.append(&mut admin_public_keys_refrence); let enc_details = EncryptedCandidateDetails::new(form, recipients).await?; - - Mutation::add_candidate_details(db, candidate, enc_details) + + ParentService::add_parent_details(db, candidate.application, enc_details.clone()).await?; + Mutation::add_candidate_details(db, candidate, enc_details.clone()) .await .map_err(|_| ServiceError::DbError) } pub async fn decrypt_details( db: &DbConn, - candidate_id: i32, + application_id: i32, password: String, ) -> Result { - let candidate = match Query::find_candidate_by_id(db, candidate_id).await { + let candidate = match Query::find_candidate_by_id(db, application_id).await { Ok(candidate) => candidate.unwrap(), Err(_) => return Err(ServiceError::DbError), // TODO: logging }; + let parent = Query::find_parent_by_id(db, application_id).await.unwrap().unwrap(); match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { Ok(valid) => { @@ -114,7 +116,7 @@ impl CandidateService { .await .ok() .unwrap(); - let enc_details = EncryptedCandidateDetails::try_from(candidate)?; + let enc_details = EncryptedCandidateDetails::try_from((candidate, parent))?; enc_details.decrypt(dec_priv_key).await } @@ -162,7 +164,7 @@ impl CandidateService { }; let Some(candidate) = candidate else { - return Err(ServiceError::UserNotFound); + return Err(ServiceError::CandidateNotFound); }; let private_key_encrypted = candidate.private_key; @@ -221,7 +223,7 @@ mod tests { use crate::{ crypto, - services::candidate_service::{CandidateService, CandidateDetails}, + services::candidate_service::{CandidateService, CandidateDetails}, Query, Mutation, }; use super::EncryptedCandidateDetails; @@ -239,7 +241,7 @@ mod tests { #[cfg(test)] async fn get_memory_sqlite_connection() -> DbConn { - use entity::{admin, candidate}; + use entity::{admin, candidate, parent}; use sea_orm::Schema; use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend}; @@ -248,8 +250,8 @@ mod tests { let schema = Schema::new(DbBackend::Sqlite); let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity); - let stmt2: TableCreateStatement = schema.create_table_from_entity(admin::Entity); + let stmt3: TableCreateStatement = schema.create_table_from_entity(parent::Entity); db.execute(db.get_database_backend().build(&stmt)) .await @@ -257,6 +259,9 @@ mod tests { db.execute(db.get_database_backend().build(&stmt2)) .await .unwrap(); + db.execute(db.get_database_backend().build(&stmt3)) + .await + .unwrap(); db } @@ -268,6 +273,9 @@ mod tests { let secret_message = "trnka".to_string(); + Mutation::create_parent(&db, 1) + .await.unwrap(); + let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) .await .ok() @@ -303,7 +311,7 @@ mod tests { let form = CandidateDetails { name: "test".to_string(), - surname: "a".to_string(), + surname: "aaa".to_string(), birthplace: "b".to_string(), birthdate: NaiveDate::from_ymd(1999, 1, 1), address: "test".to_string(), @@ -312,10 +320,15 @@ mod tests { email: "test".to_string(), sex: "test".to_string(), study: "test".to_string(), + parent_name: "test".to_string(), + parent_surname: "test".to_string(), + parent_telephone: "test".to_string(), + parent_email: "test".to_string(), + }; + CandidateService::add_candidate_details(&db, candidate, form) .await - .ok() .unwrap() } @@ -331,16 +344,15 @@ mod tests { let password = "test".to_string(); let db = get_memory_sqlite_connection().await; let enc_candidate = put_user_data(&db).await; + let enc_parent = Query::find_parent_by_id(&db, enc_candidate.application).await.unwrap().unwrap(); let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await .unwrap(); - let dec_candidate = EncryptedCandidateDetails::try_from(enc_candidate) - .unwrap() - .decrypt(dec_priv_key) - .await - .unwrap(); + let enc_details = EncryptedCandidateDetails::try_from((enc_candidate, enc_parent)).ok().unwrap(); + let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap(); - assert_eq!(dec_candidate.name, "test"); + assert_eq!(dec_details.name, "test"); // TODO: test every element + assert_eq!(dec_details.parent_surname, "test"); } } diff --git a/core/src/services/mod.rs b/core/src/services/mod.rs index d6cd137..28fe987 100644 --- a/core/src/services/mod.rs +++ b/core/src/services/mod.rs @@ -1,3 +1,5 @@ pub mod session_service; pub mod candidate_service; -pub mod admin_service; \ No newline at end of file +pub mod admin_service; +pub mod parent_service; +pub mod application_service; \ No newline at end of file diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs new file mode 100644 index 0000000..c2877ca --- /dev/null +++ b/core/src/services/parent_service.rs @@ -0,0 +1,36 @@ +use entity::{parent}; +use sea_orm::DbConn; + +use crate::{error::ServiceError, Mutation, candidate_details::EncryptedCandidateDetails, Query}; + +pub struct ParentService; + +impl ParentService { + pub async fn create_parent( + db: &DbConn, + application_id: i32, + ) -> Result { + let parent = Mutation::create_parent(db, application_id) + .await + .map_err(|_| ServiceError::DbError)?; + + Ok(parent) + } + + pub async fn add_parent_details( + db: &DbConn, + application_id: i32, + enc_details: EncryptedCandidateDetails, + ) -> Result { + let parent = Query::find_parent_by_id(db, application_id) + .await + .map_err(|_| ServiceError::DbError)? + .ok_or(ServiceError::ParentNotFound)?; + + let parent = Mutation::add_parent_details(db, parent, enc_details) + .await + .map_err(|_| ServiceError::DbError)?; + + Ok(parent) + } +} \ No newline at end of file diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index 2474b29..35363ac 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -57,7 +57,7 @@ impl SessionService { let candidate = match Query::find_candidate_by_id(db, user_id.unwrap()).await { Ok(candidate) => match candidate { Some(candidate) => candidate, - None => return Err(ServiceError::UserNotFound), + None => return Err(ServiceError::CandidateNotFound), }, Err(_) => return Err(ServiceError::DbError), }; @@ -78,7 +78,7 @@ impl SessionService { let admin = match Query::find_admin_by_id(db, admin_id.unwrap()).await { Ok(admin) => match admin { Some(admin) => admin, - None => return Err(ServiceError::UserNotFound), + None => return Err(ServiceError::CandidateNotFound), }, Err(_) => return Err(ServiceError::DbError), }; @@ -162,7 +162,7 @@ impl SessionService { #[cfg(test)] mod tests { - use entity::{admin, candidate, session}; + use entity::{admin, candidate, session, parent}; use sea_orm::{ prelude::Uuid, sea_query::TableCreateStatement, ConnectionTrait, Database, DbBackend, @@ -183,6 +183,7 @@ mod tests { let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity); let stmt2: TableCreateStatement = schema.create_table_from_entity(admin::Entity); let stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity); + let stmt4: TableCreateStatement = schema.create_table_from_entity(parent::Entity); db.execute(db.get_database_backend().build(&stmt)) .await .unwrap(); @@ -192,6 +193,9 @@ mod tests { db.execute(db.get_database_backend().build(&stmt3)) .await .unwrap(); + db.execute(db.get_database_backend().build(&stmt4)) + .await + .unwrap(); db } @@ -218,9 +222,8 @@ mod tests { async fn test_candidate_session_correct_password() { let db = &get_memory_sqlite_connection().await; - CandidateService::create(&db, 103151, &"Tajny_kod".to_string(), "".to_string()) + CandidateService::create(db, 103151, &"Tajny_kod".to_string(), "".to_string()) .await - .ok() .unwrap(); // correct password @@ -231,9 +234,8 @@ mod tests { "Tajny_kod".to_string(), "127.0.0.1".to_string(), ) - .await - .ok() - .unwrap(); + .await + .unwrap(); // println!("{}", session.err().unwrap().1); assert!( SessionService::auth_user_session(db, Uuid::parse_str(&session).unwrap()) @@ -249,7 +251,6 @@ mod tests { let candidate_form = CandidateService::create(&db, 103151, &"Tajny_kod".to_string(), "".to_string()) .await - .ok() .unwrap(); // incorrect password diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 002093d..6a044fa 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -3,7 +3,7 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "candidate")] pub struct Model { - #[sea_orm(column_type = "Integer", primary_key, auto_increment = false)] + #[sea_orm(primary_key, auto_increment = false)] pub application: i32, pub code: String, pub name: Option, @@ -17,8 +17,8 @@ pub struct Model { pub email: Option, pub sex: Option, pub study: Option, - #[sea_orm(column_type = "Text", nullable)] pub personal_identification_number: Option, + #[sea_orm(column_type = "Text")] pub personal_identification_number_hash: String, pub public_key: String, pub private_key: String, @@ -28,18 +28,10 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(has_one = "super::parent::Entity")] - Parent, #[sea_orm(has_many = "super::session::Entity")] Session, } -impl Related for Entity { - fn to() -> RelationDef { - Relation::Parent.def() - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Session.def() diff --git a/entity/src/parent.rs b/entity/src/parent.rs index 4d107af..7e1b00c 100644 --- a/entity/src/parent.rs +++ b/entity/src/parent.rs @@ -14,19 +14,6 @@ pub struct Model { } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::candidate::Entity", - from = "Column::Application", - to = "super::candidate::Column::Application" - )] - Candidate, -} - -impl Related for Entity { - fn to() -> RelationDef { - Relation::Candidate.def() - } -} +pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 85e2a9c..511d8c0 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -7,7 +7,6 @@ mod m20221024_134454_insert_sample_admin; mod m20221025_154422_create_session; mod m20221027_194728_session_create_user_fk; mod m20221028_194728_session_create_admin_fk; -mod m20221030_133428_parent_create_candidate_fk; pub struct Migrator; #[async_trait::async_trait] @@ -21,7 +20,6 @@ impl MigratorTrait for Migrator { Box::new(m20221025_154422_create_session::Migration), Box::new(m20221027_194728_session_create_user_fk::Migration), Box::new(m20221028_194728_session_create_admin_fk::Migration), - Box::new(m20221030_133428_parent_create_candidate_fk::Migration), ] } } diff --git a/migration/src/m20221030_133428_parent_create_candidate_fk.rs b/migration/src/m20221030_133428_parent_create_candidate_fk.rs deleted file mode 100644 index c390fa0..0000000 --- a/migration/src/m20221030_133428_parent_create_candidate_fk.rs +++ /dev/null @@ -1,26 +0,0 @@ -use sea_orm_migration::prelude::*; - -use crate::{m20221024_124701_create_parent::Parent, m20221024_121621_create_candidate::Candidate}; - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager.create_foreign_key(ForeignKey::create() - .name("candidate_fk") - .from(Parent::Table, Parent::Application) - .to(Candidate::Table, Candidate::Application) - .on_delete(ForeignKeyAction::Cascade) - .on_update(ForeignKeyAction::Cascade) - .to_owned()).await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager.drop_foreign_key(ForeignKey::drop() - .name("candidate_fk") - .table(Parent::Table) - .to_owned()).await - } -} \ No newline at end of file From 97b087f4c25990bbda98a0b1e59b5720523ef55a Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 23:59:16 +0100 Subject: [PATCH 07/12] refactor: application service --- api/src/routes/admin.rs | 4 +- api/src/routes/candidate.rs | 11 +-- core/src/services/application_service.rs | 90 +++++++++++++++++++++++- core/src/services/candidate_service.rs | 71 ++++--------------- core/src/services/parent_service.rs | 11 +-- core/src/services/session_service.rs | 14 ++-- 6 files changed, 118 insertions(+), 83 deletions(-) diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 9d110df..b57261f 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use portfolio_core::{ crypto::random_8_char_string, - services::{admin_service::AdminService, candidate_service::CandidateService}, + services::{admin_service::AdminService, candidate_service::CandidateService, application_service::ApplicationService}, }; use requests::{AdminLoginRequest, RegisterRequest}; use rocket::http::{Cookie, Status, CookieJar}; @@ -75,7 +75,7 @@ pub async fn create_candidate( let plain_text_password = random_8_char_string(); - CandidateService::create( + ApplicationService::create_candidate_with_parent( db, form.application_id, &plain_text_password, diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 671c1a7..f4322c5 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; use portfolio_core::candidate_details::CandidateDetails; +use portfolio_core::services::application_service::ApplicationService; use portfolio_core::services::candidate_service::{CandidateService}; use requests::LoginRequest; use rocket::http::{Cookie, CookieJar, Status}; @@ -65,13 +66,13 @@ pub async fn fill_details( ) -> Result> { let db = conn.into_inner(); let form = details.into_inner(); - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::candidate::Model = session.into(); // TODO: don't return candidate from session - let candidate = CandidateService::add_candidate_details(db, candidate, form).await; + let candidate_parent = ApplicationService::add_all_details(db, candidate.application, form).await; - if candidate.is_err() { + if candidate_parent.is_err() { // TODO cleanup - let e = candidate.err().unwrap(); + let e = candidate_parent.err().unwrap(); return Err(Custom( Status::from_code(e.code()).unwrap_or_default(), e.message(), @@ -92,7 +93,7 @@ pub async fn get_details( let password = password_form.password.clone(); // let handle = tokio::spawn(async move { - let details = CandidateService::decrypt_details(db, candidate.application, password) + let details = ApplicationService::decrypt_all_details(db, candidate.application, password) .await .map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message())); diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 664bae6..1e7a413 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -1,5 +1,89 @@ -/* pub struct ApplicationService; +use entity::{candidate, parent}; +use sea_orm::DbConn; + +use crate::{error::ServiceError, candidate_details::{CandidateDetails, EncryptedCandidateDetails}, Query, crypto}; + +use super::{parent_service::ParentService, candidate_service::CandidateService}; + +pub struct ApplicationService; impl ApplicationService { - pub fn create -} */ \ No newline at end of file + pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩‍🍼 + db: &DbConn, + application_id: i32, + plain_text_password: &String, + personal_id_number: String, + ) -> Result<(candidate::Model, parent::Model), ServiceError> { + Ok( + ( + CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?, + ParentService::create(db, application_id).await? + ) + ) + } + + pub async fn add_all_details( + db: &DbConn, + application: i32, + form: CandidateDetails, + ) -> Result<(candidate::Model, parent::Model), ServiceError> { + let candidate = Query::find_candidate_by_id(db, application) + .await + .map_err(|_| ServiceError::DbError)? + .ok_or(ServiceError::CandidateNotFound)?; + + let parent = Query::find_parent_by_id(db, application) + .await + .map_err(|_| ServiceError::DbError)? + .ok_or(ServiceError::ParentNotFound)?; + + let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { + return Err(ServiceError::DbError); + }; + + let mut admin_public_keys_refrence: Vec<&str> = + admin_public_keys.iter().map(|s| &**s).collect(); + + let mut recipients = vec![&*candidate.public_key]; + recipients.append(&mut admin_public_keys_refrence); + + let enc_details = EncryptedCandidateDetails::new(form, recipients).await?; + + Ok( + ( + CandidateService::add_candidate_details(db, candidate, enc_details.clone()).await?, + ParentService::add_parent_details(db, parent, enc_details.clone()).await? + ) + ) + } + + pub async fn decrypt_all_details( + db: &DbConn, + application_id: i32, + password: String, + ) -> Result { + let candidate = match Query::find_candidate_by_id(db, application_id).await { + Ok(candidate) => candidate.unwrap(), + Err(_) => return Err(ServiceError::DbError), // TODO: logging + }; + let parent = Query::find_parent_by_id(db, application_id).await.unwrap().unwrap(); + + match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { + Ok(valid) => { + if !valid { + return Err(ServiceError::InvalidCredentials); + } + } + Err(_) => return Err(ServiceError::InvalidCredentials), + } + + let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password) + .await + .ok() + .unwrap(); + let enc_details = EncryptedCandidateDetails::try_from((candidate, parent))?; + + enc_details.decrypt(dec_priv_key).await + } + +} \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index d0ed56a..1652f84 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -19,7 +19,7 @@ impl CandidateService { /// Hashed password /// Encrypted private key /// Public key - pub async fn create( + pub(in crate::services) async fn create( db: &DbConn, application_id: i32, plain_text_password: &String, @@ -53,9 +53,6 @@ impl CandidateService { return Err(ServiceError::CryptoHashFailed); }; - ParentService::create_parent(db, application_id) - .await?; - Mutation::create_candidate( db, application_id, @@ -68,59 +65,16 @@ impl CandidateService { .map_err(|_| ServiceError::DbError) } - pub async fn add_candidate_details( + pub(in crate::services) async fn add_candidate_details( db: &DbConn, candidate: candidate::Model, - form: CandidateDetails, + enc_details: EncryptedCandidateDetails, ) -> Result { - let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { - return Err(ServiceError::DbError); - }; - - let mut admin_public_keys_refrence: Vec<&str> = - admin_public_keys.iter().map(|s| &**s).collect(); - - let mut recipients = vec![&*candidate.public_key]; - - recipients.append(&mut admin_public_keys_refrence); - - let enc_details = EncryptedCandidateDetails::new(form, recipients).await?; - - ParentService::add_parent_details(db, candidate.application, enc_details.clone()).await?; Mutation::add_candidate_details(db, candidate, enc_details.clone()) .await .map_err(|_| ServiceError::DbError) } - pub async fn decrypt_details( - db: &DbConn, - application_id: i32, - password: String, - ) -> Result { - let candidate = match Query::find_candidate_by_id(db, application_id).await { - Ok(candidate) => candidate.unwrap(), - Err(_) => return Err(ServiceError::DbError), // TODO: logging - }; - let parent = Query::find_parent_by_id(db, application_id).await.unwrap().unwrap(); - - match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { - Ok(valid) => { - if !valid { - return Err(ServiceError::InvalidCredentials); - } - } - Err(_) => return Err(ServiceError::InvalidCredentials), - } - - let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password) - .await - .ok() - .unwrap(); - let enc_details = EncryptedCandidateDetails::try_from((candidate, parent))?; - - enc_details.decrypt(dec_priv_key).await - } - pub fn is_set_up(candidate: &candidate::Model) -> bool { candidate.name.is_some() && candidate.surname.is_some() && @@ -218,7 +172,6 @@ impl CandidateService { #[cfg(test)] mod tests { - use entity::candidate::Model; use sea_orm::{Database, DbConn}; use crate::{ @@ -227,6 +180,10 @@ mod tests { }; use super::EncryptedCandidateDetails; + use chrono::NaiveDate; + use entity::{parent, candidate}; + + use crate::services::application_service::ApplicationService; #[tokio::test] async fn test_application_id_validation() { @@ -300,11 +257,9 @@ mod tests { } #[cfg(test)] - async fn put_user_data(db: &DbConn) -> Model { - use chrono::NaiveDate; - + async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) { let plain_text_password = "test".to_string(); - let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) + let (candidate, parent) = ApplicationService::create_candidate_with_parent(&db, 103151, &plain_text_password, "".to_string()) .await .ok() .unwrap(); @@ -327,7 +282,7 @@ mod tests { }; - CandidateService::add_candidate_details(&db, candidate, form) + ApplicationService::add_all_details(&db, candidate.application, form) .await .unwrap() } @@ -335,16 +290,16 @@ mod tests { #[tokio::test] async fn test_put_user_data() { let db = get_memory_sqlite_connection().await; - let candidate = put_user_data(&db).await; + let (candidate, parent) = put_user_data(&db).await; assert!(candidate.name.is_some()); + assert!(parent.name.is_some()); } #[tokio::test] async fn test_encrypt_decrypt_user_data() { let password = "test".to_string(); let db = get_memory_sqlite_connection().await; - let enc_candidate = put_user_data(&db).await; - let enc_parent = Query::find_parent_by_id(&db, enc_candidate.application).await.unwrap().unwrap(); + let (enc_candidate, enc_parent) = put_user_data(&db).await; let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index c2877ca..ba6363e 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -1,12 +1,12 @@ use entity::{parent}; use sea_orm::DbConn; -use crate::{error::ServiceError, Mutation, candidate_details::EncryptedCandidateDetails, Query}; +use crate::{error::ServiceError, Mutation, candidate_details::EncryptedCandidateDetails}; pub struct ParentService; impl ParentService { - pub async fn create_parent( + pub async fn create( db: &DbConn, application_id: i32, ) -> Result { @@ -19,14 +19,9 @@ impl ParentService { pub async fn add_parent_details( db: &DbConn, - application_id: i32, + parent: parent::Model, enc_details: EncryptedCandidateDetails, ) -> Result { - let parent = Query::find_parent_by_id(db, application_id) - .await - .map_err(|_| ServiceError::DbError)? - .ok_or(ServiceError::ParentNotFound)?; - let parent = Mutation::add_parent_details(db, parent, enc_details) .await .map_err(|_| ServiceError::DbError)?; diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index 35363ac..a661f10 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -171,7 +171,7 @@ mod tests { use crate::{ crypto, - services::{candidate_service::CandidateService, session_service::SessionService}, + services::{session_service::SessionService, application_service::ApplicationService}, }; #[cfg(test)] @@ -205,10 +205,10 @@ mod tests { let db = get_memory_sqlite_connection().await; - let candidate = CandidateService::create(&db, 103151, &SECRET.to_string(), "".to_string()) + let candidate = ApplicationService::create_candidate_with_parent(&db, 103151, &SECRET.to_string(), "".to_string()) .await .ok() - .unwrap(); + .unwrap().0; assert_eq!(candidate.application, 103151); assert_ne!(candidate.code, SECRET.to_string()); @@ -222,9 +222,9 @@ mod tests { async fn test_candidate_session_correct_password() { let db = &get_memory_sqlite_connection().await; - CandidateService::create(db, 103151, &"Tajny_kod".to_string(), "".to_string()) + ApplicationService::create_candidate_with_parent(db, 103151, &"Tajny_kod".to_string(), "".to_string()) .await - .unwrap(); + .unwrap().0; // correct password let session = SessionService::new_session( @@ -249,9 +249,9 @@ mod tests { let db = &get_memory_sqlite_connection().await; let candidate_form = - CandidateService::create(&db, 103151, &"Tajny_kod".to_string(), "".to_string()) + ApplicationService::create_candidate_with_parent(&db, 103151, &"Tajny_kod".to_string(), "".to_string()) .await - .unwrap(); + .unwrap().0; // incorrect password assert!(SessionService::new_session( From ef2cca94ead7ebe8b773ab789c20eceb96903920 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 12 Nov 2022 00:11:16 +0100 Subject: [PATCH 08/12] refactor: decrypt private key method --- core/src/services/candidate_service.rs | 29 +++++++++++--------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 1652f84..338eb9a 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -4,10 +4,10 @@ use sea_orm::{prelude::Uuid, DbConn}; use crate::{ crypto::{self, hash_password}, error::ServiceError, - Mutation, Query, candidate_details::{CandidateDetails, EncryptedCandidateDetails}, + Mutation, Query, candidate_details::{EncryptedCandidateDetails}, }; -use super::{session_service::{AdminUser, SessionService}, parent_service::ParentService}; +use super::{session_service::{AdminUser, SessionService}}; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; @@ -79,7 +79,7 @@ impl CandidateService { candidate.name.is_some() && candidate.surname.is_some() && candidate.birthplace.is_some() && - // birthdate: NaiveDate::from_ymd(2000, 1, 1), + candidate.birthdate.is_some() && candidate.address.is_some() && candidate.telephone.is_some() && candidate.citizenship.is_some() && @@ -107,20 +107,9 @@ impl CandidateService { } async fn decrypt_private_key( - db: &DbConn, - candidate_id: i32, + candidate: candidate::Model, password: String, ) -> Result { - let candidate = Query::find_candidate_by_id(db, candidate_id).await; - - let Ok(candidate) = candidate else { - return Err(ServiceError::DbError); - }; - - let Some(candidate) = candidate else { - return Err(ServiceError::CandidateNotFound); - }; - let private_key_encrypted = candidate.private_key; let private_key = crypto::decrypt_password(private_key_encrypted, password).await; @@ -138,11 +127,16 @@ impl CandidateService { password: String, ip_addr: String, ) -> Result<(String, String), ServiceError> { + let candidate = Query::find_candidate_by_id(db, candidate_id) + .await + .map_err(|_| ServiceError::DbError)? + .ok_or(ServiceError::CandidateNotFound)?; + let session_id = SessionService::new_session(db, Some(candidate_id), None, password.clone(), ip_addr).await; match session_id { Ok(session_id) => { - let private_key = Self::decrypt_private_key(db, candidate_id, password).await?; + let private_key = Self::decrypt_private_key(candidate, password).await?; Ok((session_id, private_key)) } Err(e) => Err(e), @@ -176,7 +170,7 @@ mod tests { use crate::{ crypto, - services::candidate_service::{CandidateService, CandidateDetails}, Query, Mutation, + services::candidate_service::{CandidateService}, Mutation, }; use super::EncryptedCandidateDetails; @@ -184,6 +178,7 @@ mod tests { use entity::{parent, candidate}; use crate::services::application_service::ApplicationService; + use crate::candidate_details::CandidateDetails; #[tokio::test] async fn test_application_id_validation() { From 12d25c9ffc2509eed9ff8d9ef845a9ad6c59d6af Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 12 Nov 2022 00:13:32 +0100 Subject: [PATCH 09/12] refactor: rename to aplication details --- api/src/routes/candidate.rs | 6 +++--- core/src/candidate_details.rs | 18 +++++++++--------- core/src/database/mutation/candidate.rs | 4 ++-- core/src/database/mutation/parent.rs | 4 ++-- core/src/services/application_service.rs | 10 +++++----- core/src/services/candidate_service.rs | 12 ++++++------ core/src/services/parent_service.rs | 4 ++-- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index f4322c5..85486a8 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -1,6 +1,6 @@ use std::net::SocketAddr; -use portfolio_core::candidate_details::CandidateDetails; +use portfolio_core::candidate_details::ApplicationDetails; use portfolio_core::services::application_service::ApplicationService; use portfolio_core::services::candidate_service::{CandidateService}; use requests::LoginRequest; @@ -61,7 +61,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(); @@ -87,7 +87,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 index 7b20ab2..2bf1961 100644 --- a/core/src/candidate_details.rs +++ b/core/src/candidate_details.rs @@ -58,7 +58,7 @@ impl TryFrom> for EncryptedString { // TODO: take a look at th } #[derive(Clone)] -pub struct EncryptedCandidateDetails { +pub struct EncryptedApplicationDetails { // Candidate pub name: EncryptedString, pub surname: EncryptedString, @@ -78,8 +78,8 @@ pub struct EncryptedCandidateDetails { pub parent_email: EncryptedString, } -impl EncryptedCandidateDetails { - pub async fn new(form: CandidateDetails, recipients: Vec<&str>) -> Result { +impl EncryptedApplicationDetails { + pub async fn new(form: ApplicationDetails, recipients: Vec<&str>) -> Result { let birthdate_str = form.birthdate.format(NAIVE_DATE_FMT).to_string(); let d = tokio::try_join!( EncryptedString::new(&form.name, &recipients), @@ -99,7 +99,7 @@ impl EncryptedCandidateDetails { EncryptedString::new(&form.parent_email, &recipients), )?; - Ok(EncryptedCandidateDetails { + Ok(EncryptedApplicationDetails { name: d.0, surname: d.1, birthplace: d.2, @@ -118,7 +118,7 @@ impl EncryptedCandidateDetails { }) } - pub async fn decrypt(self, priv_key: String) -> Result { + pub async fn decrypt(self, priv_key: String) -> Result { let d = tokio::try_join!( self.name.decrypt(&priv_key), // 0 self.surname.decrypt(&priv_key), // 1 @@ -137,7 +137,7 @@ impl EncryptedCandidateDetails { self.parent_email.decrypt(&priv_key), )?; - Ok(CandidateDetails { + Ok(ApplicationDetails { name: d.0, surname: d.1, birthplace: d.2, @@ -157,11 +157,11 @@ impl EncryptedCandidateDetails { } } -impl TryFrom<(candidate::Model, parent::Model)> for EncryptedCandidateDetails { +impl TryFrom<(candidate::Model, parent::Model)> for EncryptedApplicationDetails { type Error = ServiceError; fn try_from((candidate, parent): (candidate::Model, parent::Model)) -> Result { - Ok(EncryptedCandidateDetails { + Ok(EncryptedApplicationDetails { name: EncryptedString::try_from(candidate.name)?, surname: EncryptedString::try_from(candidate.surname)?, birthplace: EncryptedString::try_from(candidate.birthplace)?, @@ -182,7 +182,7 @@ impl TryFrom<(candidate::Model, parent::Model)> for EncryptedCandidateDetails { } #[derive(Debug, Serialize, Deserialize)] -pub struct CandidateDetails { +pub struct ApplicationDetails { // Candidate pub name: String, pub surname: String, diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index 9b191b0..8ffda50 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -1,4 +1,4 @@ -use crate::{Mutation, candidate_details::{EncryptedCandidateDetails}}; +use crate::{Mutation, candidate_details::{EncryptedApplicationDetails}}; 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: EncryptedCandidateDetails, + enc_details: EncryptedApplicationDetails, ) -> Result { let mut user: candidate::ActiveModel = user.into(); user.name = Set(Some(enc_details.name.into())); diff --git a/core/src/database/mutation/parent.rs b/core/src/database/mutation/parent.rs index 2855a29..a464e42 100644 --- a/core/src/database/mutation/parent.rs +++ b/core/src/database/mutation/parent.rs @@ -1,4 +1,4 @@ -use crate::{Mutation, candidate_details::EncryptedCandidateDetails}; +use crate::{Mutation, candidate_details::EncryptedApplicationDetails}; use ::entity::parent::{self, Model}; use sea_orm::*; @@ -18,7 +18,7 @@ impl Mutation { pub async fn add_parent_details( db: &DbConn, parent: Model, - enc_details: EncryptedCandidateDetails, // TODO: use seperate struct?? + enc_details: EncryptedApplicationDetails, // TODO: use seperate struct?? ) -> Result { let mut user: parent::ActiveModel = parent.into(); user.name = Set(Some(enc_details.parent_name.into())); diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 1e7a413..0c7b083 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -1,7 +1,7 @@ use entity::{candidate, parent}; use sea_orm::DbConn; -use crate::{error::ServiceError, candidate_details::{CandidateDetails, EncryptedCandidateDetails}, Query, crypto}; +use crate::{error::ServiceError, candidate_details::{ApplicationDetails, EncryptedApplicationDetails}, Query, crypto}; use super::{parent_service::ParentService, candidate_service::CandidateService}; @@ -25,7 +25,7 @@ impl ApplicationService { pub async fn add_all_details( db: &DbConn, application: i32, - form: CandidateDetails, + form: ApplicationDetails, ) -> Result<(candidate::Model, parent::Model), ServiceError> { let candidate = Query::find_candidate_by_id(db, application) .await @@ -47,7 +47,7 @@ impl ApplicationService { let mut recipients = vec![&*candidate.public_key]; recipients.append(&mut admin_public_keys_refrence); - let enc_details = EncryptedCandidateDetails::new(form, recipients).await?; + let enc_details = EncryptedApplicationDetails::new(form, recipients).await?; Ok( ( @@ -61,7 +61,7 @@ impl ApplicationService { db: &DbConn, application_id: i32, password: String, - ) -> Result { + ) -> Result { let candidate = match Query::find_candidate_by_id(db, application_id).await { Ok(candidate) => candidate.unwrap(), Err(_) => return Err(ServiceError::DbError), // TODO: logging @@ -81,7 +81,7 @@ impl ApplicationService { .await .ok() .unwrap(); - let enc_details = EncryptedCandidateDetails::try_from((candidate, parent))?; + let enc_details = EncryptedApplicationDetails::try_from((candidate, parent))?; enc_details.decrypt(dec_priv_key).await } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 338eb9a..b5ced20 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -4,7 +4,7 @@ use sea_orm::{prelude::Uuid, DbConn}; use crate::{ crypto::{self, hash_password}, error::ServiceError, - Mutation, Query, candidate_details::{EncryptedCandidateDetails}, + Mutation, Query, candidate_details::{EncryptedApplicationDetails}, }; use super::{session_service::{AdminUser, SessionService}}; @@ -68,7 +68,7 @@ impl CandidateService { pub(in crate::services) async fn add_candidate_details( db: &DbConn, candidate: candidate::Model, - enc_details: EncryptedCandidateDetails, + enc_details: EncryptedApplicationDetails, ) -> Result { Mutation::add_candidate_details(db, candidate, enc_details.clone()) .await @@ -173,12 +173,12 @@ mod tests { services::candidate_service::{CandidateService}, Mutation, }; - use super::EncryptedCandidateDetails; + use super::EncryptedApplicationDetails; use chrono::NaiveDate; use entity::{parent, candidate}; use crate::services::application_service::ApplicationService; - use crate::candidate_details::CandidateDetails; + use crate::candidate_details::ApplicationDetails; #[tokio::test] async fn test_application_id_validation() { @@ -259,7 +259,7 @@ mod tests { .ok() .unwrap(); - let form = CandidateDetails { + let form = ApplicationDetails { name: "test".to_string(), surname: "aaa".to_string(), birthplace: "b".to_string(), @@ -299,7 +299,7 @@ mod tests { let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await .unwrap(); - let enc_details = EncryptedCandidateDetails::try_from((enc_candidate, enc_parent)).ok().unwrap(); + let enc_details = EncryptedApplicationDetails::try_from((enc_candidate, enc_parent)).ok().unwrap(); let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap(); assert_eq!(dec_details.name, "test"); // TODO: test every element diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index ba6363e..79ce638 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -1,7 +1,7 @@ use entity::{parent}; use sea_orm::DbConn; -use crate::{error::ServiceError, Mutation, candidate_details::EncryptedCandidateDetails}; +use crate::{error::ServiceError, Mutation, candidate_details::EncryptedApplicationDetails}; pub struct ParentService; @@ -20,7 +20,7 @@ impl ParentService { pub async fn add_parent_details( db: &DbConn, parent: parent::Model, - enc_details: EncryptedCandidateDetails, + enc_details: EncryptedApplicationDetails, ) -> Result { let parent = Mutation::add_parent_details(db, parent, enc_details) .await From c238b8ec45245936f86292b18725ef39c0a8cf06 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 12 Nov 2022 11:27:13 +0100 Subject: [PATCH 10/12] feat: candidate fk on parent --- core/src/services/candidate_service.rs | 7 ++--- entity/src/admin.rs | 4 ++- entity/src/candidate.rs | 10 +++++++ entity/src/mod.rs | 2 ++ entity/src/parent.rs | 19 +++++++++++++- entity/src/prelude.rs | 2 ++ entity/src/session.rs | 18 ++++++------- migration/src/lib.rs | 2 ++ ...21112_112212_create_parent_candidate_fk.rs | 26 +++++++++++++++++++ 9 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 migration/src/m20221112_112212_create_parent_candidate_fk.rs diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index b5ced20..f0e1e9d 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -225,14 +225,15 @@ mod tests { let secret_message = "trnka".to_string(); - Mutation::create_parent(&db, 1) - .await.unwrap(); - + let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) .await .ok() .unwrap(); + Mutation::create_parent(&db, 103151) + .await.unwrap(); + let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key]) .await diff --git a/entity/src/admin.rs b/entity/src/admin.rs index cc51dae..8486730 100644 --- a/entity/src/admin.rs +++ b/entity/src/admin.rs @@ -1,9 +1,11 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "admin")] pub struct Model { - #[sea_orm(column_type = "Integer", primary_key)] + #[sea_orm(primary_key)] pub id: i32, pub name: String, pub public_key: String, diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 6a044fa..6b7d5bd 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] @@ -28,10 +30,18 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm(has_many = "super::parent::Entity")] + Parent, #[sea_orm(has_many = "super::session::Entity")] Session, } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Parent.def() + } +} + impl Related for Entity { fn to() -> RelationDef { Relation::Session.def() diff --git a/entity/src/mod.rs b/entity/src/mod.rs index ef5f522..63cf621 100644 --- a/entity/src/mod.rs +++ b/entity/src/mod.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + pub mod prelude; pub mod admin; diff --git a/entity/src/parent.rs b/entity/src/parent.rs index 7e1b00c..f748482 100644 --- a/entity/src/parent.rs +++ b/entity/src/parent.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] @@ -14,6 +16,21 @@ pub struct Model { } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub enum Relation { + #[sea_orm( + belongs_to = "super::candidate::Entity", + from = "Column::Application", + to = "super::candidate::Column::Application", + on_update = "Cascade", + on_delete = "Cascade" + )] + Candidate, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Candidate.def() + } +} impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs index af8586b..73b608f 100644 --- a/entity/src/prelude.rs +++ b/entity/src/prelude.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + pub use super::admin::Entity as Admin; pub use super::candidate::Entity as Candidate; pub use super::parent::Entity as Parent; diff --git a/entity/src/session.rs b/entity/src/session.rs index f4ecf37..e0b5829 100644 --- a/entity/src/session.rs +++ b/entity/src/session.rs @@ -1,3 +1,5 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] @@ -5,10 +7,8 @@ use sea_orm::entity::prelude::*; pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: Uuid, - #[sea_orm(column_type = "Integer", nullable)] - pub admin_id: Option, - #[sea_orm(column_type = "Integer", nullable)] pub user_id: Option, + pub admin_id: Option, pub ip_address: String, pub created_at: DateTime, pub expires_at: DateTime, @@ -34,16 +34,16 @@ pub enum Relation { Candidate, } -impl Related for Entity { - fn to() -> RelationDef { - Relation::Candidate.def() - } -} - impl Related for Entity { fn to() -> RelationDef { Relation::Admin.def() } } +impl Related for Entity { + fn to() -> RelationDef { + Relation::Candidate.def() + } +} + impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 511d8c0..6e2081d 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -7,6 +7,7 @@ mod m20221024_134454_insert_sample_admin; mod m20221025_154422_create_session; mod m20221027_194728_session_create_user_fk; mod m20221028_194728_session_create_admin_fk; +mod m20221112_112212_create_parent_candidate_fk; pub struct Migrator; #[async_trait::async_trait] @@ -20,6 +21,7 @@ impl MigratorTrait for Migrator { Box::new(m20221025_154422_create_session::Migration), Box::new(m20221027_194728_session_create_user_fk::Migration), Box::new(m20221028_194728_session_create_admin_fk::Migration), + Box::new(m20221112_112212_create_parent_candidate_fk::Migration), ] } } diff --git a/migration/src/m20221112_112212_create_parent_candidate_fk.rs b/migration/src/m20221112_112212_create_parent_candidate_fk.rs new file mode 100644 index 0000000..c390fa0 --- /dev/null +++ b/migration/src/m20221112_112212_create_parent_candidate_fk.rs @@ -0,0 +1,26 @@ +use sea_orm_migration::prelude::*; + +use crate::{m20221024_124701_create_parent::Parent, m20221024_121621_create_candidate::Candidate}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager.create_foreign_key(ForeignKey::create() + .name("candidate_fk") + .from(Parent::Table, Parent::Application) + .to(Candidate::Table, Candidate::Application) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) + .to_owned()).await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager.drop_foreign_key(ForeignKey::drop() + .name("candidate_fk") + .table(Parent::Table) + .to_owned()).await + } +} \ No newline at end of file From 2d9b755e5a81d5635b3cd459dc56c1142868349a Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 12 Nov 2022 11:38:56 +0100 Subject: [PATCH 11/12] feat: async create candidate, async add details --- core/src/services/application_service.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 0c7b083..eed8881 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -15,10 +15,10 @@ impl ApplicationService { personal_id_number: String, ) -> Result<(candidate::Model, parent::Model), ServiceError> { Ok( - ( - CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?, - ParentService::create(db, application_id).await? - ) + tokio::try_join!( + CandidateService::create(db, application_id, plain_text_password, personal_id_number), + ParentService::create(db, application_id) + )? ) } @@ -50,10 +50,10 @@ impl ApplicationService { let enc_details = EncryptedApplicationDetails::new(form, recipients).await?; Ok( - ( - CandidateService::add_candidate_details(db, candidate, enc_details.clone()).await?, - ParentService::add_parent_details(db, parent, enc_details.clone()).await? - ) + tokio::try_join!( + CandidateService::add_candidate_details(db, candidate, enc_details.clone()), + ParentService::add_parent_details(db, parent, enc_details.clone()) + )? ) } From 3bef8d9534b9caca2a424ec55053a33020029d10 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 12 Nov 2022 11:42:39 +0100 Subject: [PATCH 12/12] fix: create candidate error --- api/src/guards/request/auth/admin.rs | 2 +- core/src/services/application_service.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api/src/guards/request/auth/admin.rs b/api/src/guards/request/auth/admin.rs index 8f75a31..717e41a 100644 --- a/api/src/guards/request/auth/admin.rs +++ b/api/src/guards/request/auth/admin.rs @@ -51,7 +51,7 @@ impl<'r> FromRequest<'r> for AdminAuth { match session { Ok(model) => Outcome::Success(AdminAuth(model, private_key.to_string())), Err(e) => Outcome::Failure( - (Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None) + (Status::from_code(e.code()).unwrap_or(Status::Unauthorized), None) ), } diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index eed8881..5793162 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -15,10 +15,16 @@ impl ApplicationService { personal_id_number: String, ) -> Result<(candidate::Model, parent::Model), ServiceError> { Ok( - tokio::try_join!( + /* tokio::try_join!( // TODO: try_join! is not working CandidateService::create(db, application_id, plain_text_password, personal_id_number), ParentService::create(db, application_id) - )? + )? */ + + + ( + CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?, + ParentService::create(db, application_id).await? + ) ) }