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(