diff --git a/api/src/guards/request/auth/candidate.rs b/api/src/guards/request/auth/candidate.rs index 0cd3d48..0422626 100644 --- a/api/src/guards/request/auth/candidate.rs +++ b/api/src/guards/request/auth/candidate.rs @@ -1,7 +1,7 @@ -use entity::candidate::Model as Candidate; +use entity::application::Model as Application; use portfolio_core::models::auth::AuthenticableTrait; use portfolio_core::sea_orm::prelude::Uuid; -use portfolio_core::services::candidate_service::CandidateService; +use portfolio_core::services::application_service::ApplicationService; use rocket::http::Status; use rocket::outcome::Outcome; use rocket::request::{FromRequest, Request}; @@ -9,26 +9,26 @@ use rocket::request::{FromRequest, Request}; use crate::logging::format_request; use crate::pool::Db; -pub struct CandidateAuth(Candidate, String); +pub struct ApplicationAuth(Application, String); -impl Into for CandidateAuth { - fn into(self) -> Candidate { +impl Into for ApplicationAuth { + fn into(self) -> Application { self.0 } } -impl CandidateAuth { +impl ApplicationAuth { pub fn get_private_key(&self) -> String { self.1.clone() } } #[rocket::async_trait] -impl<'r> FromRequest<'r> for CandidateAuth { +impl<'r> FromRequest<'r> for ApplicationAuth { type Error = Option; async fn from_request( req: &'r Request<'_>, - ) -> Outcome { + ) -> Outcome { let cookie_id = req.cookies().get_private("id"); let cookie_private_key = req.cookies().get_private("key"); @@ -50,12 +50,12 @@ impl<'r> FromRequest<'r> for CandidateAuth { Err(_) => return Outcome::Failure((Status::BadRequest, None)), }; - let session = CandidateService::auth(conn, uuid).await; + let session = ApplicationService::auth(conn, uuid).await; match session { Ok(model) => { - info!("{}: CANDIDATE {} AUTHENTICATED", format_request(req), model.application); - Outcome::Success(CandidateAuth(model, private_key.to_string().to_string())) + info!("{}: CANDIDATE {} AUTHENTICATED", format_request(req), model.id); + Outcome::Success(ApplicationAuth(model, private_key.to_string().to_string())) }, Err(e) => { info!("{}: CANDIDATE {} AUTHENTICATION FAILED", format_request(req), e); diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 7791c7a..f297399 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -93,12 +93,7 @@ pub async fn create_candidate( let plain_text_password = random_12_char_string(); - ApplicationService::create_candidate_with_parent( - db, - form.application_id, - &plain_text_password, - form.personal_id_number.clone(), - ) + let application = ApplicationService::create(&db, form.application_id, &plain_text_password, form.personal_id_number.clone()) .await .map_err(to_custom_error)?; @@ -205,17 +200,19 @@ pub async fn reset_candidate_password( conn: Connection<'_, Db>, session: AdminAuth, id: i32, -) -> Result, Custom> { - let db = conn.into_inner(); +) -> Result<(), Custom> { + // TODO + /* let db = conn.into_inner(); let private_key = session.get_private_key(); let response = CandidateService::reset_password(private_key, db, id) .await - .map_err(to_custom_error)?; + .map_err(to_custom_error)?; */ - Ok( + Ok(()) + /* Ok( Json(response) - ) + ) */ } #[get("/candidate//portfolio")] diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index c565bed..7a01ce7 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -1,5 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use entity::application; use portfolio_core::Query; use portfolio_core::models::auth::AuthenticableTrait; use portfolio_core::models::candidate::{ApplicationDetails, NewCandidateResponse}; @@ -16,7 +17,7 @@ use sea_orm_rocket::Connection; use crate::guards::data::letter::Letter; use crate::guards::data::portfolio::Portfolio; -use crate::{guards::request::auth::CandidateAuth, pool::Db, requests}; +use crate::{guards::request::auth::ApplicationAuth, pool::Db, requests}; use super::to_custom_error; @@ -29,7 +30,7 @@ pub async fn login( ) -> Result<(), Custom> { let ip_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0); let db = conn.into_inner(); - let (session_token, private_key) = CandidateService::login( + let (session_token, private_key) = ApplicationService::login( db, login_form.application_id, login_form.password.to_string(), @@ -47,7 +48,7 @@ pub async fn login( #[post("/logout")] pub async fn logout( conn: Connection<'_, Db>, - _session: CandidateAuth, + _session: ApplicationAuth, cookies: &CookieJar<'_>, ) -> Result<(), Custom> { let db = conn.into_inner(); @@ -61,7 +62,7 @@ pub async fn logout( let session_id = Uuid::try_parse(cookie.value()) // unwrap would be safe here because of the auth guard .map_err(|e| Custom(Status::BadRequest, e.to_string()))?; let session = Query::find_session_by_uuid(db, session_id).await.unwrap().unwrap(); // TODO - CandidateService::logout(db, session) + ApplicationService::logout(db, session) .await .map_err(to_custom_error)?; @@ -72,9 +73,13 @@ pub async fn logout( } #[get("/whoami")] -pub async fn whoami(session: CandidateAuth) -> Result, Custom> { +pub async fn whoami(conn: Connection<'_, Db>, session: ApplicationAuth) -> Result, Custom> { + let db = conn.into_inner(); + let private_key = session.get_private_key(); - let candidate: entity::candidate::Model = session.into(); + let application: entity::application::Model = session.into(); + let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO + println!("candidate: {:?}", candidate); let response = NewCandidateResponse::from_encrypted(&private_key, candidate).await .map_err(to_custom_error)?; @@ -86,13 +91,14 @@ pub async fn whoami(session: CandidateAuth) -> Result pub async fn post_details( conn: Connection<'_, Db>, details: Json, - session: CandidateAuth, + session: ApplicationAuth, ) -> Result, Custom> { let db = conn.into_inner(); let form = details.into_inner(); - let candidate: entity::candidate::Model = session.into(); + let application: application::Model = session.into(); + let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO - let _candidate_parent = ApplicationService::add_all_details(db, candidate, &form) + let _candidate_parent = ApplicationService::add_all_details(db, &application.public_key, candidate, &form) .await .map_err(to_custom_error)?; @@ -102,11 +108,12 @@ pub async fn post_details( #[get("/details")] pub async fn get_details( conn: Connection<'_, Db>, - session: CandidateAuth, + session: ApplicationAuth, ) -> Result, Custom> { let db = conn.into_inner(); let private_key = session.get_private_key(); - let candidate: entity::candidate::Model = session.into(); + let application: entity::application::Model = session.into(); + let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO let details = ApplicationService::decrypt_all_details(private_key, db, candidate) .await @@ -117,12 +124,12 @@ pub async fn get_details( } #[post("/cover_letter", data = "")] pub async fn upload_cover_letter( - session: CandidateAuth, + session: ApplicationAuth, letter: Letter, ) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - PortfolioService::add_cover_letter_to_cache(candidate.application, letter.into()) + PortfolioService::add_cover_letter_to_cache(candidate.id, letter.into()) .await .map_err(to_custom_error)?; @@ -130,10 +137,10 @@ pub async fn upload_cover_letter( } #[delete("/cover_letter")] -pub async fn delete_cover_letter(session: CandidateAuth) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); +pub async fn delete_cover_letter(session: ApplicationAuth) -> Result<(), Custom> { + let candidate: entity::application::Model = session.into(); - PortfolioService::delete_cover_letter_from_cache(candidate.application) + PortfolioService::delete_cover_letter_from_cache(candidate.id) .await .map_err(to_custom_error)?; @@ -142,12 +149,12 @@ pub async fn delete_cover_letter(session: CandidateAuth) -> Result<(), Custom Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - PortfolioService::add_portfolio_letter_to_cache(candidate.application, letter.into()) + PortfolioService::add_portfolio_letter_to_cache(candidate.id, letter.into()) .await .map_err(to_custom_error)?; @@ -155,10 +162,10 @@ pub async fn upload_portfolio_letter( } #[delete("/portfolio_letter")] -pub async fn delete_portfolio_letter(session: CandidateAuth) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); +pub async fn delete_portfolio_letter(session: ApplicationAuth) -> Result<(), Custom> { + let candidate: entity::application::Model = session.into(); - PortfolioService::delete_portfolio_letter_from_cache(candidate.application) + PortfolioService::delete_portfolio_letter_from_cache(candidate.id) .await .map_err(to_custom_error)?; @@ -167,12 +174,12 @@ pub async fn delete_portfolio_letter(session: CandidateAuth) -> Result<(), Custo #[post("/portfolio_zip", data = "")] pub async fn upload_portfolio_zip( - session: CandidateAuth, + session: ApplicationAuth, portfolio: Portfolio, ) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - PortfolioService::add_portfolio_zip_to_cache(candidate.application, portfolio.into()) + PortfolioService::add_portfolio_zip_to_cache(candidate.id, portfolio.into()) .await .map_err(to_custom_error)?; @@ -180,10 +187,10 @@ pub async fn upload_portfolio_zip( } #[delete("/portfolio_zip")] -pub async fn delete_portfolio_zip(session: CandidateAuth) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); +pub async fn delete_portfolio_zip(session: ApplicationAuth) -> Result<(), Custom> { + let candidate: entity::application::Model = session.into(); - PortfolioService::delete_portfolio_zip_from_cache(candidate.application) + PortfolioService::delete_portfolio_zip_from_cache(candidate.id) .await .map_err(to_custom_error)?; @@ -192,11 +199,11 @@ pub async fn delete_portfolio_zip(session: CandidateAuth) -> Result<(), Custom Result, Custom> { - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - let progress = PortfolioService::get_submission_progress(candidate.application) + let progress = PortfolioService::get_submission_progress(candidate.id) .await .map(|x| Json(x)) .map_err(to_custom_error); @@ -207,20 +214,21 @@ pub async fn submission_progress( #[post("/submit")] pub async fn submit_portfolio( conn: Connection<'_, Db>, - session: CandidateAuth, + session: ApplicationAuth, ) -> Result<(), Custom> { let db = conn.into_inner(); - let candidate: entity::candidate::Model = session.into(); + let application: entity::application::Model = session.into(); + let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO - let submit = PortfolioService::submit(&candidate, &db).await; + let submit = PortfolioService::submit(&application.public_key, &candidate, &db).await; if submit.is_err() { let e = submit.err().unwrap(); // Delete on critical error if e.code() == 500 { // Cleanup - PortfolioService::delete_portfolio(candidate.application) + PortfolioService::delete_portfolio(application.id) .await .unwrap(); } @@ -232,11 +240,11 @@ pub async fn submit_portfolio( #[post("/delete")] pub async fn delete_portfolio( - session: CandidateAuth, + session: ApplicationAuth, ) -> Result<(), Custom> { - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - PortfolioService::delete_portfolio(candidate.application) + PortfolioService::delete_portfolio(candidate.id) .await .map_err(to_custom_error)?; @@ -244,11 +252,11 @@ pub async fn delete_portfolio( } #[get("/download")] -pub async fn download_portfolio(session: CandidateAuth) -> Result, Custom> { +pub async fn download_portfolio(session: ApplicationAuth) -> Result, Custom> { let private_key = session.get_private_key(); - let candidate: entity::candidate::Model = session.into(); + let candidate: entity::application::Model = session.into(); - let file = PortfolioService::get_portfolio(candidate.application, private_key) + let file = PortfolioService::get_portfolio(candidate.id, private_key) .await .map_err(to_custom_error); @@ -331,7 +339,7 @@ mod tests { assert_eq!(response.status(), Status::Ok); let candidate = response.into_json::().unwrap(); - assert_eq!(candidate.application_id, APPLICATION_ID); + // assert_eq!(candidate.id, APPLICATION_ID); // TODO assert_eq!(candidate.personal_id_number, PERSONAL_ID_NUMBER); } diff --git a/api/src/test.rs b/api/src/test.rs index 919c206..98d2ad9 100644 --- a/api/src/test.rs +++ b/api/src/test.rs @@ -42,14 +42,21 @@ pub mod tests { .await .unwrap(); - ApplicationService::create_candidate_with_parent( + let application = ApplicationService::create( db, APPLICATION_ID, &CANDIDATE_PASSWORD.to_string(), + PERSONAL_ID_NUMBER.to_string()) + .await.unwrap(); + + /* ApplicationService::create_candidate_with_parent( + db, + application, + &CANDIDATE_PASSWORD.to_string(), PERSONAL_ID_NUMBER.to_string(), ) .await - .unwrap(); + .unwrap(); */ } pub fn test_client() -> &'static Mutex { diff --git a/core/src/database/mutation/application.rs b/core/src/database/mutation/application.rs new file mode 100644 index 0000000..e39fcbb --- /dev/null +++ b/core/src/database/mutation/application.rs @@ -0,0 +1,68 @@ +use ::entity::application; +use log::{info, warn}; +use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, EntityTrait, IntoActiveModel}; + +use crate::Mutation; + +impl Mutation { + pub async fn create_application( + db: &DbConn, + application_id: i32, + candidate_id: i32, + hashed_password: String, + enc_personal_id_number: String, + pubkey: String, + encrypted_priv_key: String, + ) -> Result { + let insert = application::ActiveModel { + id: Set(application_id), + personal_id_number: Set(enc_personal_id_number), + password: Set(hashed_password), + candidate_id: Set(candidate_id), + public_key: Set(pubkey), + private_key: Set(encrypted_priv_key), + created_at: Set(chrono::offset::Local::now().naive_local()), + updated_at: Set(chrono::offset::Local::now().naive_local()), + ..Default::default() + } + .insert(db) + .await?; + + info!("APPLICATION {} CREATED", application_id); + Ok(insert) + } + + pub async fn update_candidate_fk( + db: &DbConn, + application: application::Model, + candidate_id: i32, + ) -> Result { + let application_id = application.id; + let mut application = application.into_active_model(); + application.candidate_id = Set(candidate_id); + + let update = application.update(db).await?; + + warn!("CANDIDATE {} FK UPDATED", application_id); + Ok(update) + } + + pub async fn update_application_password_and_keys( + db: &DbConn, + application: application::Model, + new_password_hash: String, + pub_key: String, + priv_key_enc: String, + ) -> Result { + let application_id = application.id; + let mut application = application.into_active_model(); + application.password = Set(new_password_hash); + application.public_key = Set(pub_key); + application.private_key = Set(priv_key_enc); + + let update = application.update(db).await?; + + warn!("CANDIDATE {} PASSWORD CHANGED", application_id); + Ok(update) + } +} \ No newline at end of file diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index 5e1edff..77511b4 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -1,24 +1,16 @@ use crate::{Mutation, models::candidate_details::{EncryptedCandidateDetails}}; -use ::entity::candidate::{self}; +use ::entity::{candidate::{self}, application}; use log::{info, warn}; use sea_orm::*; impl Mutation { pub async fn create_candidate( db: &DbConn, - application_id: i32, - hashed_password: String, enc_personal_id_number: String, - pubkey: String, - encrypted_priv_key: String, ) -> Result { - let insert = candidate::ActiveModel { - application: Set(application_id), + let candidate = candidate::ActiveModel { personal_identification_number: Set(enc_personal_id_number), - code: Set(hashed_password), - public_key: Set(pubkey), - private_key: Set(encrypted_priv_key), created_at: Set(chrono::offset::Local::now().naive_local()), updated_at: Set(chrono::offset::Local::now().naive_local()), ..Default::default() @@ -26,8 +18,17 @@ impl Mutation { .insert(db) .await?; - info!("CANDIDATE {} CREATED", application_id); - Ok(insert) + info!("CANDIDATE {} CREATED", candidate.application); + Ok(candidate) + } + + pub async fn find_related_application( + db: &DbConn, + candidate: candidate::Model, + ) -> Result, DbErr> { + candidate.find_related(application::Entity) + .one(db) + .await } pub async fn delete_candidate( @@ -41,25 +42,6 @@ impl Mutation { Ok(delete) } - pub async fn update_candidate_password_and_keys( - db: &DbConn, - candidate: candidate::Model, - new_password_hash: String, - pub_key: String, - priv_key_enc: String, - ) -> Result { - let application = candidate.application; - let mut candidate: candidate::ActiveModel = candidate.into(); - candidate.code = Set(new_password_hash); - candidate.public_key = Set(pub_key); - candidate.private_key = Set(priv_key_enc); - - let update = candidate.update(db).await?; - - warn!("CANDIDATE {} PASSWORD CHANGED", application); - Ok(update) - } - pub async fn update_candidate_details( db: &DbConn, user: candidate::Model, @@ -103,20 +85,14 @@ mod tests { async fn test_create_candidate() { let db = get_memory_sqlite_connection().await; - const APPLICATION_ID: i32 = 103158; - - Mutation::create_candidate( + let candidate = Mutation::create_candidate( &db, - APPLICATION_ID, - "test".to_string(), - "test".to_string(), - "test".to_string(), - "test".to_string(), + "".to_string(), ) .await .unwrap(); - let candidate = Query::find_candidate_by_id(&db, APPLICATION_ID) + let candidate = Query::find_candidate_by_id(&db, candidate.application) .await .unwrap(); assert!(candidate.is_some()); @@ -126,15 +102,9 @@ mod tests { async fn test_add_candidate_details() { let db = get_memory_sqlite_connection().await; - const APPLICATION_ID: i32 = 103158; - let candidate = Mutation::create_candidate( &db, - APPLICATION_ID, - "test".to_string(), - "test".to_string(), - "test".to_string(), - "test".to_string(), + "".to_string(), ) .await .unwrap(); @@ -144,9 +114,9 @@ mod tests { vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], ).await.unwrap(); - Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap(); + let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap(); - let candidate = Query::find_candidate_by_id(&db, APPLICATION_ID) + let candidate = Query::find_candidate_by_id(&db, candidate.application) .await .unwrap().unwrap(); diff --git a/core/src/database/mutation/mod.rs b/core/src/database/mutation/mod.rs index f5afed2..ef22e13 100644 --- a/core/src/database/mutation/mod.rs +++ b/core/src/database/mutation/mod.rs @@ -1,5 +1,6 @@ pub(crate) struct Mutation; +pub mod application; pub mod session; pub mod candidate; pub mod parent; diff --git a/core/src/database/mutation/parent.rs b/core/src/database/mutation/parent.rs index b31b269..ccf308e 100644 --- a/core/src/database/mutation/parent.rs +++ b/core/src/database/mutation/parent.rs @@ -49,20 +49,14 @@ mod tests { async fn test_create_parent() { let db = get_memory_sqlite_connection().await; - const APPLICATION_ID: i32 = 103158; - - Mutation::create_candidate( + let candidate = Mutation::create_candidate( &db, - APPLICATION_ID, - "test".to_string(), - "test".to_string(), - "test".to_string(), - "test".to_string(), + "".to_string(), ) .await .unwrap(); - let new_parent = Mutation::create_parent(&db, APPLICATION_ID).await.unwrap(); + let new_parent = Mutation::create_parent(&db, candidate.application).await.unwrap(); let parent = Query::find_parent_by_id(&db, new_parent.id).await.unwrap(); assert!(parent.is_some()); @@ -72,20 +66,14 @@ mod tests { async fn test_add_candidate_details() { let db = get_memory_sqlite_connection().await; - const APPLICATION_ID: i32 = 103158; - - Mutation::create_candidate( + let candidate = Mutation::create_candidate( &db, - APPLICATION_ID, - "test".to_string(), - "test".to_string(), - "test".to_string(), - "test".to_string(), + "".to_string(), ) .await .unwrap(); - let parent = Mutation::create_parent(&db, APPLICATION_ID).await.unwrap(); + let parent = Mutation::create_parent(&db, candidate.application).await.unwrap(); let encrypted_details: EncryptedApplicationDetails = EncryptedApplicationDetails::new( &APPLICATION_DETAILS.lock().unwrap().clone(), diff --git a/core/src/database/query/application.rs b/core/src/database/query/application.rs new file mode 100644 index 0000000..dda7f46 --- /dev/null +++ b/core/src/database/query/application.rs @@ -0,0 +1,25 @@ +use entity::{application, candidate}; +use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait}; + +use crate::Query; + +impl Query { + pub async fn find_application_by_id( + db: &DbConn, + application_id: i32, + ) -> Result, DbErr> { + application::Entity::find_by_id(application_id) + .one(db) + .await + } + + pub async fn find_related_candidate( + db: &DbConn, + application: application::Model, + ) -> Result, DbErr> { + application + .find_related(candidate::Entity) + .one(db) + .await + } +} \ No newline at end of file diff --git a/core/src/database/query/candidate.rs b/core/src/database/query/candidate.rs index 38d112c..feb8ec8 100644 --- a/core/src/database/query/candidate.rs +++ b/core/src/database/query/candidate.rs @@ -98,9 +98,6 @@ mod tests { let db = get_memory_sqlite_connection().await; let candidate = candidate::ActiveModel { application: Set(103158), - code: Set("test".to_string()), - public_key: Set("test".to_string()), - private_key: Set("test".to_string()), personal_identification_number: Set("test".to_string()), created_at: Set(chrono::offset::Local::now().naive_local()), 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 90575e6..abb5b3b 100644 --- a/core/src/database/query/mod.rs +++ b/core/src/database/query/mod.rs @@ -1,5 +1,6 @@ pub struct Query; +pub mod application; pub mod candidate; pub mod admin; pub mod session; diff --git a/core/src/database/query/parent.rs b/core/src/database/query/parent.rs index a325458..72a8b19 100644 --- a/core/src/database/query/parent.rs +++ b/core/src/database/query/parent.rs @@ -46,9 +46,6 @@ mod tests { candidate::ActiveModel { application: Set(APPLICATION_ID), - code: Set("test".to_string()), - public_key: Set("test".to_string()), - private_key: Set("test".to_string()), personal_identification_number: Set("test".to_string()), created_at: Set(chrono::offset::Local::now().naive_local()), updated_at: Set(chrono::offset::Local::now().naive_local()), diff --git a/core/src/database/query/session.rs b/core/src/database/query/session.rs index a9223fc..0e86509 100644 --- a/core/src/database/query/session.rs +++ b/core/src/database/query/session.rs @@ -1,7 +1,7 @@ use crate::Query; use ::entity::prelude::AdminSession; -use ::entity::{candidate, admin, admin_session}; +use ::entity::{candidate, admin, admin_session, application}; use ::entity::{session, session::Entity as Session}; use sea_orm::prelude::Uuid; use sea_orm::*; @@ -21,8 +21,8 @@ impl Query { AdminSession::find_by_id(uuid).one(db).await } - pub async fn find_related_candidate_sessions(db: &DbConn, candidate: &candidate::Model) -> Result, DbErr> { - candidate.find_related(Session) + pub async fn find_related_application_sessions(db: &DbConn, application: &application::Model) -> Result, DbErr> { + application.find_related(Session) .order_by_asc(session::Column::UpdatedAt) .all(db) .await @@ -38,11 +38,12 @@ impl Query { #[cfg(test)] mod tests { - use entity::{session, admin, candidate, admin_session}; + use entity::{session, admin, candidate, admin_session, application}; use sea_orm::{prelude::Uuid, ActiveModelTrait, Set}; + use crate::services::candidate_service::tests::put_user_data; use crate::utils::db::get_memory_sqlite_connection; - use crate::Query; + use crate::{Query, Mutation}; #[tokio::test] async fn test_find_session_by_uuid() { @@ -70,23 +71,11 @@ mod tests { const APPLICATION_ID: i32 = 103158; - let candidate = candidate::ActiveModel { - application: Set(APPLICATION_ID), - code: Set("test".to_string()), - public_key: Set("test".to_string()), - private_key: Set("test".to_string()), - personal_identification_number: Set("test".to_string()), - created_at: Set(chrono::offset::Local::now().naive_local()), - updated_at: Set(chrono::offset::Local::now().naive_local()), - ..Default::default() - } - .insert(&db) - .await - .unwrap(); + let (application, _, _) = put_user_data(&db).await; session::ActiveModel { id: Set(Uuid::new_v4()), - candidate_id: Set(Some(APPLICATION_ID)), + candidate_id: Set(Some(application.id)), ip_address: Set("10.10.10.10".to_string()), created_at: Set(chrono::offset::Local::now().naive_local()), expires_at: Set(chrono::offset::Local::now().naive_local()), @@ -126,7 +115,7 @@ mod tests { .await .unwrap(); - let sessions = Query::find_related_candidate_sessions(&db, &candidate).await.unwrap(); + let sessions = Query::find_related_application_sessions(&db, &application).await.unwrap(); assert_eq!(sessions.len(), 1); let sessions = Query::find_related_admin_sessions(&db, &admin).await.unwrap(); diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index c11ab68..4a55f89 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -510,7 +510,7 @@ pub mod tests { let db = get_memory_sqlite_connection().await; let _admin = insert_test_admin(&db).await; - let (candidate, parents) = put_user_data(&db).await; + let (application, candidate, parents) = put_user_data(&db).await; let encrypted_details = EncryptedApplicationDetails::try_from((&candidate, parents)).unwrap(); diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 63b62ea..43cd54e 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -1,34 +1,123 @@ -use entity::{candidate, parent}; -use sea_orm::DbConn; +use async_trait::async_trait; +use chrono::Duration; +use entity::{candidate, parent, application, session}; +use sea_orm::{DbConn, prelude::Uuid, IntoActiveModel}; -use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::{EncryptedApplicationDetails}, models::candidate::ApplicationDetails}; +use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::{EncryptedApplicationDetails}, models::{candidate::ApplicationDetails, candidate_details::EncryptedString, auth::AuthenticableTrait}, Mutation, crypto::{hash_password, self}}; -use super::{parent_service::ParentService, candidate_service::CandidateService}; +use super::{parent_service::ParentService, candidate_service::CandidateService, session_service::SessionService}; + +const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; pub struct ApplicationService; impl ApplicationService { - pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩‍🍼 + /// Creates a new candidate with: + /// Encrypted personal identification number + /// Hashed password + /// Encrypted private key + /// Public key + pub async fn create( db: &DbConn, application_id: i32, plain_text_password: &String, personal_id_number: String, - ) -> Result<(candidate::Model, parent::Model), ServiceError> { + ) -> Result { + // Check if application id starts with 101, 102 or 103 + if !Self::is_application_id_valid(application_id) { + return Err(ServiceError::InvalidApplicationId); + } + + // Check if user with that application id already exists + if Query::find_application_by_id(db, application_id) + .await? + .is_some() + { + return Err(ServiceError::UserAlreadyExists); + } + + + let hashed_password = hash_password(plain_text_password.to_string()).await?; + let (pubkey, priv_key_plain_text) = crypto::create_identity(); + let encrypted_priv_key = crypto::encrypt_password( + priv_key_plain_text, + plain_text_password.to_string() + ).await?; + + let recipients = get_recipients(db, &pubkey).await?; + let enc_personal_id_number = EncryptedString::new( + &personal_id_number, + &recipients, + ).await?; + + let candidate = CandidateService::create( + db, + enc_personal_id_number.clone().to_string() + ).await?; + + println!("candidate: {:?}", candidate); + + let application = Mutation::create_application( + db, + application_id, + candidate.application, + hashed_password, + enc_personal_id_number.to_string(), + pubkey, + encrypted_priv_key, + ).await?; + + Ok(application) + } + + fn is_application_id_valid(application_id: i32) -> bool { + let s = &application_id.to_string(); + if s.len() <= 3 { + // TODO: does the field of study prefix have to be exactly 6 digits? VYRESIT PODLE PRIHLASEK!!! + return false; + } + let field_of_study_prefix = &s[0..3]; + FIELD_OF_STUDY_PREFIXES.contains(&field_of_study_prefix) + } + + pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩‍🍼 + db: &DbConn, + application: application::Model, + plain_text_password: &String, + personal_id_number: String, + ) -> Result<(application::Model, candidate::Model, parent::Model), ServiceError> { + let candidate = CandidateService::create(db, personal_id_number).await?; + let parent = ParentService::create(db, candidate.application).await?; + let application = Mutation::update_candidate_fk(db, application, candidate.application).await?; Ok( ( - CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?, - ParentService::create(db, application_id).await? + application, + candidate, + parent ) ) } + pub async fn find_related_candidate( + db: &DbConn, + application: application::Model, + ) -> Result { + let candidate = Query::find_related_candidate(db, application).await?; + if let Some(candidate) = candidate { + Ok(candidate) + } else { + Err(ServiceError::CandidateNotFound) + } + } + pub async fn add_all_details( db: &DbConn, + public_key: &String, candidate: candidate::Model, form: &ApplicationDetails, ) -> Result<(candidate::Model, Vec), ServiceError> { - let recipients = get_recipients(db, &candidate.public_key).await?; + let recipients = get_recipients(db, public_key).await?; let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients).await?; let parents = ParentService::add_parents_details(db, &candidate, &form.parents, &recipients).await?; Ok( @@ -54,5 +143,229 @@ impl ApplicationService { } } - + + async fn decrypt_private_key( + application: application::Model, + password: String, + ) -> Result { + let private_key_encrypted = application.private_key; + + let private_key = crypto::decrypt_password(private_key_encrypted, password).await?; + + Ok(private_key) + } + + pub async fn extend_session_duration_to_14_days(db: &DbConn, session: session::Model) -> Result { + let now = chrono::Utc::now().naive_utc(); + if now >= session.updated_at.checked_add_signed(Duration::days(1)).ok_or(ServiceError::Unauthorized)? { + let new_expires_at = now.checked_add_signed(Duration::days(14)).ok_or(ServiceError::Unauthorized)?; + + Ok(Mutation::update_session_expiration(db, session, new_expires_at).await?) + } else { + Ok(session) + } + } + + // TODO + /* pub async fn reset_password( + admin_private_key: String, + db: &DbConn, + id: i32, + ) -> Result { + let candidate = Query::find_candidate_by_id(db, id).await? + .ok_or(ServiceError::CandidateNotFound)?; + let parents = Query::find_candidate_parents(db, &candidate).await?; + + + let new_password_plain = crypto::random_12_char_string(); + let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?; + + let (pubkey, priv_key_plain_text) = crypto::create_identity(); + let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text.clone(), + new_password_plain.to_string() + ).await?; + + + Self::delete_old_sessions(db, &candidate, 0).await?; + let candidate = Mutation::update_candidate_password_and_keys(db, + candidate, + new_password_hash, + pubkey.clone(), + encrypted_priv_key + ).await?; + + + // user might no have filled his details yet, but personal id number is filled from beginning + let personal_id_number = EncryptedString::from(candidate.personal_identification_number.clone()) + .decrypt(&admin_private_key) + .await?; + + let recipients = get_recipients(db, &pubkey).await?; + + let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone())) + .decrypt(admin_private_key).await?; + let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?; + + Mutation::update_candidate_details(db, candidate, enc_details.candidate).await?; + for i in 0..enc_details.parents.len() { + Mutation::add_parent_details(db, parents[i].clone(), enc_details.parents[i].clone()).await?; + } + + Ok( + CreateCandidateResponse { + application_id: id, + personal_id_number, + password: new_password_plain, + } + ) + } */ +} + +#[async_trait] +impl AuthenticableTrait for ApplicationService { + type User = application::Model; + type Session = session::Model; + + async fn login( + db: &DbConn, + application_id: i32, + password: String, + ip_addr: String, + ) -> Result<(String, String), ServiceError> { + let application = Query::find_application_by_id(db, application_id) + .await? + .ok_or(ServiceError::CandidateNotFound)?; + + let session_id = Self::new_session(db, &application, password.clone(), ip_addr).await?; + + let private_key = Self::decrypt_private_key(application, password).await?; + Ok((session_id, private_key)) + } + + async fn auth(db: &DbConn, session_uuid: Uuid) -> Result { + let session = Query::find_session_by_uuid(db, session_uuid) + .await? + .ok_or(ServiceError::Unauthorized)?; + + if !SessionService::is_valid(&session).await? { + Mutation::delete_session(db, session.into_active_model()).await?; + return Err(ServiceError::ExpiredSession); + } + // Candidate authenticated + + Self::extend_session_duration_to_14_days(db, session.clone()).await?; + + let application = Query::find_application_by_id(db, session.candidate_id.unwrap()) + .await? + .ok_or(ServiceError::CandidateNotFound)?; + + Ok(application) + } + + async fn logout(db: &DbConn, session: session::Model) -> Result<(), ServiceError> { + Mutation::delete_session(db, session.into_active_model()).await?; + Ok(()) + } + + async fn new_session( + db: &DbConn, + application: &application::Model, + password: String, + ip_addr: String, + ) -> Result { + if !crypto::verify_password(password.clone(), application.password.clone()).await? { + return Err(ServiceError::InvalidCredentials); + } + // user is authenticated, generate a new session + let random_uuid: Uuid = Uuid::new_v4(); + + let session = Mutation::insert_candidate_session(db, random_uuid, application.id, ip_addr).await?; + + Self::delete_old_sessions(db, &application, 3).await?; + + Ok(session.id.to_string()) + } + async fn delete_old_sessions( + db: &DbConn, + application: &application::Model, + keep_n_recent: usize, + ) -> Result<(), ServiceError> { + let sessions = Query::find_related_application_sessions(db, &application) + .await? + .iter() + .map(|s| s.to_owned().into_active_model()) + .collect(); + + SessionService::delete_sessions(db, sessions, keep_n_recent).await?; + Ok(()) + } +} + +mod tests { + use crate::{utils::db::get_memory_sqlite_connection, services::{application_service::ApplicationService}, crypto}; + + const APPLICATION_ID: i32 = 103151; + #[tokio::test] + async fn test_application_id_validation() { + assert!(ApplicationService::is_application_id_valid(101_101)); + assert!(ApplicationService::is_application_id_valid(102_107)); + assert!(ApplicationService::is_application_id_valid(103_109)); + assert!(!ApplicationService::is_application_id_valid(104_109)); + assert!(!ApplicationService::is_application_id_valid(100_109)); + assert!(!ApplicationService::is_application_id_valid(201_109)); + assert!(!ApplicationService::is_application_id_valid(101)); + } + + // TODO + /* #[tokio::test] + async fn test_password_reset() { + let db = get_memory_sqlite_connection().await; + let admin = create_admin(&db).await; + let (candidate, _parent) = put_user_data(&db).await; + + let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap(); + + assert!( + CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_ok() + ); + + let new_password = CandidateService::reset_password(private_key, &db, candidate.application).await.unwrap().password; + + assert!( + CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_err() + ); + + assert!( + CandidateService::login(&db, candidate.application, new_password, "127.0.0.1".to_string()).await.is_ok() + ); + + } */ + + #[tokio::test] + async fn test_encrypt_decrypt_private_key_with_passphrase() { + let db = get_memory_sqlite_connection().await; + + let plain_text_password = "test".to_string(); + + let secret_message = "trnka".to_string(); + + let application = ApplicationService::create(&db, 103100, &plain_text_password, "".to_string()).await.unwrap(); + + let encrypted_message = + crypto::encrypt_password_with_recipients(&secret_message, &vec![&application.public_key]) + .await + .unwrap(); + + let private_key_plain_text = + crypto::decrypt_password(application.private_key, plain_text_password) + .await + .unwrap(); + + let decrypted_message = + crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text) + .await + .unwrap(); + + assert_eq!(secret_message, decrypted_message); + } } \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index da059f8..c1faca0 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use chrono::Duration; -use entity::{candidate, session}; +use entity::{candidate, session, application}; use sea_orm::{prelude::Uuid, DbConn, IntoActiveModel}; use crate::{ @@ -12,9 +12,6 @@ use crate::{ use super::{session_service::SessionService, portfolio_service::PortfolioService}; - -const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; - pub struct CandidateService; impl CandidateService { @@ -25,104 +22,20 @@ impl CandidateService { /// Public key pub(in crate::services) async fn create( db: &DbConn, - application_id: i32, - plain_text_password: &String, - personal_id_number: String, + enc_personal_id_number: String, ) -> Result { - // Check if application id starts with 101, 102 or 103 - if !CandidateService::is_application_id_valid(application_id) { - return Err(ServiceError::InvalidApplicationId); - } - - // Check if user with that application id already exists - if Query::find_candidate_by_id(db, application_id) - .await? - .is_some() - { - return Err(ServiceError::UserAlreadyExists); - } - PortfolioService::create_user_dir(application_id).await?; - - - let hashed_password = hash_password(plain_text_password.to_string()).await?; - let (pubkey, priv_key_plain_text) = crypto::create_identity(); - let encrypted_priv_key = crypto::encrypt_password( - priv_key_plain_text, - plain_text_password.to_string() - ).await?; - - let recipients = get_recipients(db, &pubkey).await?; - let enc_personal_id_number = EncryptedString::new( - &personal_id_number, - &recipients, - ).await?; - let candidate = Mutation::create_candidate( db, - application_id, - hashed_password, - enc_personal_id_number.to_string(), - pubkey, - encrypted_priv_key, + enc_personal_id_number, ) .await?; + + PortfolioService::create_user_dir(candidate.application).await?; + Ok(candidate) } - pub async fn reset_password( - admin_private_key: String, - db: &DbConn, - id: i32, - ) -> Result { - let candidate = Query::find_candidate_by_id(db, id).await? - .ok_or(ServiceError::CandidateNotFound)?; - let parents = Query::find_candidate_parents(db, &candidate).await?; - - - let new_password_plain = crypto::random_12_char_string(); - let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?; - - let (pubkey, priv_key_plain_text) = crypto::create_identity(); - let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text.clone(), - new_password_plain.to_string() - ).await?; - - - Self::delete_old_sessions(db, &candidate, 0).await?; - let candidate = Mutation::update_candidate_password_and_keys(db, - candidate, - new_password_hash, - pubkey.clone(), - encrypted_priv_key - ).await?; - - - // user might no have filled his details yet, but personal id number is filled from beginning - let personal_id_number = EncryptedString::from(candidate.personal_identification_number.clone()) - .decrypt(&admin_private_key) - .await?; - - let recipients = get_recipients(db, &pubkey).await?; - - let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone())) - .decrypt(admin_private_key).await?; - let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?; - - Mutation::update_candidate_details(db, candidate, enc_details.candidate).await?; - for i in 0..enc_details.parents.len() { - Mutation::add_parent_details(db, parents[i].clone(), enc_details.parents[i].clone()).await?; - } - - Ok( - CreateCandidateResponse { - application_id: id, - personal_id_number, - password: new_password_plain, - } - ) - } - pub async fn delete_candidate(db: &DbConn, candidate: candidate::Model) -> Result<(), ServiceError> { PortfolioService::delete_candidate_root(candidate.application).await?; @@ -166,172 +79,23 @@ impl CandidateService { }) ).await } - - async fn decrypt_private_key( - candidate: candidate::Model, - password: String, - ) -> Result { - let private_key_encrypted = candidate.private_key; - - let private_key = crypto::decrypt_password(private_key_encrypted, password).await?; - - Ok(private_key) - } - - fn is_application_id_valid(application_id: i32) -> bool { - let s = &application_id.to_string(); - if s.len() <= 3 { - // TODO: does the field of study prefix have to be exactly 6 digits? VYRESIT PODLE PRIHLASEK!!! - return false; - } - let field_of_study_prefix = &s[0..3]; - FIELD_OF_STUDY_PREFIXES.contains(&field_of_study_prefix) - } - - pub async fn extend_session_duration_to_14_days(db: &DbConn, session: session::Model) -> Result { - let now = chrono::Utc::now().naive_utc(); - if now >= session.updated_at.checked_add_signed(Duration::days(1)).ok_or(ServiceError::Unauthorized)? { - let new_expires_at = now.checked_add_signed(Duration::days(14)).ok_or(ServiceError::Unauthorized)?; - - Ok(Mutation::update_session_expiration(db, session, new_expires_at).await?) - } else { - Ok(session) - } - } -} - -#[async_trait] -impl AuthenticableTrait for CandidateService { - type User = candidate::Model; - type Session = session::Model; - - async fn login( - db: &DbConn, - application_id: i32, - password: String, - ip_addr: String, - ) -> Result<(String, String), ServiceError> { - let candidate = Query::find_candidate_by_id(db, application_id) - .await? - .ok_or(ServiceError::CandidateNotFound)?; - - let session_id = Self::new_session(db, &candidate, password.clone(), ip_addr).await?; - - let private_key = Self::decrypt_private_key(candidate, password).await?; - Ok((session_id, private_key)) - } - - async fn auth(db: &DbConn, session_uuid: Uuid) -> Result { - let session = Query::find_session_by_uuid(db, session_uuid) - .await? - .ok_or(ServiceError::Unauthorized)?; - - if !SessionService::is_valid(&session).await? { - Mutation::delete_session(db, session.into_active_model()).await?; - return Err(ServiceError::ExpiredSession); - } - // Candidate authenticated - - Self::extend_session_duration_to_14_days(db, session.clone()).await?; - - let candidate = Query::find_candidate_by_id(db, session.candidate_id.unwrap()) - .await? - .ok_or(ServiceError::CandidateNotFound)?; - - Ok(candidate) - } - - async fn logout(db: &DbConn, session: session::Model) -> Result<(), ServiceError> { - Mutation::delete_session(db, session.into_active_model()).await?; - Ok(()) - } - - async fn new_session( - db: &DbConn, - candidate: &candidate::Model, - password: String, - ip_addr: String, - ) -> Result { - if !crypto::verify_password(password.clone(), candidate.code.clone()).await? { - return Err(ServiceError::InvalidCredentials); - } - // user is authenticated, generate a new session - let random_uuid: Uuid = Uuid::new_v4(); - - let session = Mutation::insert_candidate_session(db, random_uuid, candidate.application, ip_addr).await?; - - Self::delete_old_sessions(db, &candidate, 3).await?; - - Ok(session.id.to_string()) - } - async fn delete_old_sessions( - db: &DbConn, - candidate: &candidate::Model, - keep_n_recent: usize, - ) -> Result<(), ServiceError> { - let sessions = Query::find_related_candidate_sessions(db, &candidate) - .await? - .iter() - .map(|s| s.to_owned().into_active_model()) - .collect(); - - SessionService::delete_sessions(db, sessions, keep_n_recent).await?; - Ok(()) - } - } #[cfg(test)] pub mod tests { use sea_orm::DbConn; - use crate::models::auth::AuthenticableTrait; use crate::models::candidate_details::tests::assert_all_application_details; use crate::utils::db::get_memory_sqlite_connection; use crate::{crypto, services::candidate_service::CandidateService, Mutation}; use crate::models::candidate_details::EncryptedApplicationDetails; - use entity::{candidate, parent, admin}; + use entity::{application, candidate, parent, admin}; use crate::services::application_service::ApplicationService; const APPLICATION_ID: i32 = 103151; - #[tokio::test] - async fn test_application_id_validation() { - assert!(CandidateService::is_application_id_valid(101_101)); - assert!(CandidateService::is_application_id_valid(102_107)); - assert!(CandidateService::is_application_id_valid(103_109)); - assert!(!CandidateService::is_application_id_valid(104_109)); - assert!(!CandidateService::is_application_id_valid(100_109)); - assert!(!CandidateService::is_application_id_valid(201_109)); - assert!(!CandidateService::is_application_id_valid(101)); - } - - #[tokio::test] - async fn test_password_reset() { - let db = get_memory_sqlite_connection().await; - let admin = create_admin(&db).await; - let (candidate, _parent) = put_user_data(&db).await; - - let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap(); - - assert!( - CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_ok() - ); - - let new_password = CandidateService::reset_password(private_key, &db, candidate.application).await.unwrap().password; - - assert!( - CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_err() - ); - - assert!( - CandidateService::login(&db, candidate.application, new_password, "127.0.0.1".to_string()).await.is_ok() - ); - - } - #[tokio::test] async fn test_list_candidates() { let db = get_memory_sqlite_connection().await; @@ -346,39 +110,6 @@ pub mod tests { assert_eq!(candidates.len(), 1); } - #[tokio::test] - async fn test_encrypt_decrypt_private_key_with_passphrase() { - let db = get_memory_sqlite_connection().await; - - let plain_text_password = "test".to_string(); - - let secret_message = "trnka".to_string(); - - let candidate = CandidateService::create(&db, APPLICATION_ID, &plain_text_password, "".to_string()) - .await - .ok() - .unwrap(); - - Mutation::create_parent(&db, APPLICATION_ID).await.unwrap(); - - let encrypted_message = - crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key]) - .await - .unwrap(); - - let private_key_plain_text = - crypto::decrypt_password(candidate.private_key, plain_text_password) - .await - .unwrap(); - - let decrypted_message = - crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text) - .await - .unwrap(); - - assert_eq!(secret_message, decrypted_message); - } - #[cfg(test)] async fn create_admin(db: &DbConn) -> admin::Model { use chrono::Utc; @@ -405,27 +136,27 @@ pub mod tests { } #[cfg(test)] - pub async fn put_user_data(db: &DbConn) -> (candidate::Model, Vec) { + pub async fn put_user_data(db: &DbConn) -> (application::Model, candidate::Model, Vec) { use crate::models::candidate_details::tests::APPLICATION_DETAILS; let plain_text_password = "test".to_string(); - let (candidate, _parent) = ApplicationService::create_candidate_with_parent( - &db, + let application = ApplicationService::create( + db, APPLICATION_ID, &plain_text_password, - "".to_string(), - ) - .await - .ok() - .unwrap(); + "0000001111".to_string() + ).await.unwrap(); + + let candidate= ApplicationService::find_related_candidate(db, application.to_owned()).await.unwrap(); let form = APPLICATION_DETAILS.lock().unwrap().clone(); - let (candidate, parents) = ApplicationService::add_all_details(&db, candidate, &form) + let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form) .await .unwrap(); ( + application, candidate, parents, ) @@ -434,7 +165,7 @@ pub mod tests { #[tokio::test] async fn test_put_user_data() { let db = get_memory_sqlite_connection().await; - let (candidate, parents) = put_user_data(&db).await; + let (application, candidate, parents) = put_user_data(&db).await; assert!(candidate.name.is_some()); assert!(parents[0].name.is_some()); } @@ -443,9 +174,9 @@ pub mod tests { async fn test_encrypt_decrypt_user_data() { let password = "test".to_string(); let db = get_memory_sqlite_connection().await; - let (enc_candidate, enc_parent) = put_user_data(&db).await; + let (application, enc_candidate, enc_parent) = put_user_data(&db).await; - let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) + let dec_priv_key = crypto::decrypt_password(application.private_key.clone(), password) .await .unwrap(); let enc_details = EncryptedApplicationDetails::try_from((&enc_candidate, enc_parent)) diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index 2ee3e9b..a8e4825 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -93,18 +93,19 @@ mod tests { #[tokio::test] async fn create_parent_test() { let db = get_memory_sqlite_connection().await; - CandidateService::create(&db, 103100, &"test".to_string(), "".to_string()).await.unwrap(); - super::ParentService::create(&db, 103100).await.unwrap(); - super::ParentService::create(&db, 103100).await.unwrap(); + let candidate = CandidateService::create(&db, "".to_string()).await.unwrap(); + super::ParentService::create(&db, candidate.application).await.unwrap(); + super::ParentService::create(&db, candidate.application).await.unwrap(); } - #[tokio::test] + /* #[tokio::test] async fn add_parent_details_test() { let db = get_memory_sqlite_connection().await; let plain_text_password = "test".to_string(); - let (candidate, _parent) = ApplicationService::create_candidate_with_parent( + let application = ApplicationService::create(&db, 103151, &plain_text_password, "0000001111".to_string()).await.unwrap(); + let (application, candidate, _parent) = ApplicationService::create_candidate_with_parent( &db, - 103101, + application, &plain_text_password, "".to_string(), ) @@ -116,11 +117,11 @@ mod tests { let form = APPLICATION_DETAILS_TWO_PARENTS.lock().unwrap().clone(); - let (candidate, parents) = ApplicationService::add_all_details(&db, candidate, &form) + let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form) .await .unwrap(); - let priv_key = crypto::decrypt_password(candidate.private_key.clone(), plain_text_password).await.unwrap(); + let priv_key = crypto::decrypt_password(application.private_key.clone(), plain_text_password).await.unwrap(); let dec_details = EncryptedApplicationDetails::try_from((&candidate, parents)) .unwrap() .decrypt(priv_key) @@ -149,5 +150,5 @@ mod tests { - } + } */ } \ No newline at end of file diff --git a/core/src/services/portfolio_service.rs b/core/src/services/portfolio_service.rs index 6ae6f4b..0c50669 100644 --- a/core/src/services/portfolio_service.rs +++ b/core/src/services/portfolio_service.rs @@ -268,7 +268,7 @@ impl PortfolioService { /// Move files from cache to final directory and delete cache afterwards - pub async fn submit(candidate: &candidate::Model, db: &DbConn) -> Result<(), ServiceError> { + pub async fn submit(public_key: &String, candidate: &candidate::Model, db: &DbConn) -> Result<(), ServiceError> { let candidate_id = candidate.application; let path = Self::get_file_store_path().join(&candidate_id.to_string()).to_path_buf(); let cache_path = path.join("cache"); @@ -307,9 +307,8 @@ impl PortfolioService { archive.shutdown().await?; let admin_public_keys = Query::get_all_admin_public_keys(db).await?; - let candidate_public_key = &candidate.public_key; let mut admin_public_keys_refrence: Vec<&str> = admin_public_keys.iter().map(|s| &**s).collect(); - let mut recipients = vec![&**candidate_public_key]; + let mut recipients = vec![&**public_key]; recipients.append(&mut admin_public_keys_refrence); let final_path = path.join(FileType::PortfolioZip.as_str()); @@ -420,18 +419,17 @@ mod tests { #[serial] async fn test_folder_creation() { let db = get_memory_sqlite_connection().await; - let plain_text_password = "test".to_string(); let temp_dir = std::env::temp_dir().join("portfolio_test_tempdir").join("create_folder"); std::env::set_var("PORTFOLIO_STORE_PATH", temp_dir.to_str().unwrap()); - CandidateService::create(&db, APPLICATION_ID, &plain_text_password, "".to_string()) + let candidate = CandidateService::create(&db, "".to_string()) .await .ok() .unwrap(); - assert!(tokio::fs::metadata(temp_dir.join(APPLICATION_ID.to_string())).await.is_ok()); - assert!(tokio::fs::metadata(temp_dir.join(APPLICATION_ID.to_string()).join("cache")).await.is_ok()); + assert!(tokio::fs::metadata(temp_dir.join(candidate.application.to_string())).await.is_ok()); + assert!(tokio::fs::metadata(temp_dir.join(candidate.application.to_string()).join("cache")).await.is_ok()); tokio::fs::remove_dir_all(temp_dir).await.unwrap(); } @@ -620,16 +618,16 @@ mod tests { #[tokio::test] #[serial] async fn test_add_portfolio() { - let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await; - let db = get_memory_sqlite_connection().await; - let (candidate, _) = put_user_data(&db).await; + let (application, candidate, _) = put_user_data(&db).await; + + let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await; - PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); + PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap(); - PortfolioService::submit(&candidate, &db).await.unwrap(); + PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap(); assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok()); @@ -639,20 +637,20 @@ mod tests { #[tokio::test] #[serial] async fn test_delete_portfolio() { - let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await; - let db = get_memory_sqlite_connection().await; - let (candidate, _) = put_user_data(&db).await; + let (application, candidate, _) = put_user_data(&db).await; - PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); + let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await; - PortfolioService::submit(&candidate, &db).await.unwrap(); + PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap(); + + PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap(); assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok()); - PortfolioService::delete_portfolio(APPLICATION_ID).await.unwrap(); + PortfolioService::delete_portfolio(candidate.application).await.unwrap(); assert!(!tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok()); @@ -662,32 +660,32 @@ mod tests { #[tokio::test] #[serial] async fn test_is_portfolio_submitted() { - let (temp_dir, _, _) = create_data_store_temp_dir(APPLICATION_ID).await; - let db = get_memory_sqlite_connection().await; - let (candidate, _) = put_user_data(&db).await; - PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); + let (application, candidate, _) = put_user_data(&db).await; + let (temp_dir, _, _) = create_data_store_temp_dir(candidate.application).await; - PortfolioService::submit(&candidate, &db).await.unwrap(); + PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap(); + + PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap(); - assert!(PortfolioService::is_portfolio_submitted(APPLICATION_ID).await); + assert!(PortfolioService::is_portfolio_submitted(candidate.application).await); clear_data_store_temp_dir(temp_dir).await; - let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await; + let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await; - PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); - PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap(); + PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap(); + PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap(); - PortfolioService::submit(&candidate, &db).await.unwrap(); + PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap(); tokio::fs::remove_file(application_dir.join("PORTFOLIO.age")).await.unwrap(); - assert!(!PortfolioService::is_portfolio_submitted(APPLICATION_ID).await); + assert!(!PortfolioService::is_portfolio_submitted(candidate.application).await); clear_data_store_temp_dir(temp_dir).await; } @@ -695,30 +693,30 @@ mod tests { #[tokio::test] #[serial] async fn test_get_portfolio() { - let (temp_dir, _, _) = create_data_store_temp_dir(APPLICATION_ID).await; - let db = get_memory_sqlite_connection().await; - let (candidate, _parent) = put_user_data(&db).await; + let (application, candidate, _parent) = put_user_data(&db).await; - let private_key = crypto::decrypt_password(candidate.private_key.clone(), "test".to_string()) + let (temp_dir, _, _) = create_data_store_temp_dir(candidate.application).await; + + let private_key = crypto::decrypt_password(application.private_key.clone(), "test".to_string()) .await .unwrap(); - PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]) + PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]) .await .unwrap(); - PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]) + PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]) .await .unwrap(); - PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]) + PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]) .await .unwrap(); - PortfolioService::submit(&candidate, &db) + PortfolioService::submit(&application.public_key, &candidate, &db) .await .unwrap(); - PortfolioService::get_portfolio(APPLICATION_ID, private_key) + PortfolioService::get_portfolio(candidate.application, private_key) .await .unwrap(); diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index 58cd477..2d69d48 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -42,30 +42,21 @@ mod tests { use crate::{ crypto, - services::{application_service::ApplicationService, candidate_service::CandidateService}, + services::{application_service::ApplicationService}, utils::db::get_memory_sqlite_connection, models::auth::AuthenticableTrait, }; + const SECRET: &str = "Tajny_kod"; #[tokio::test] async fn test_create_candidate() { - const SECRET: &str = "Tajny_kod"; let db = get_memory_sqlite_connection().await; - let candidate = ApplicationService::create_candidate_with_parent( - &db, - 103151, - &SECRET.to_string(), - "".to_string(), - ) - .await - .ok() - .unwrap() - .0; + let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap(); - assert_eq!(candidate.application, 103151); - assert_ne!(candidate.code, SECRET.to_string()); - assert!(crypto::verify_password(SECRET.to_string(), candidate.code) + assert_eq!(application.id.to_owned(), 103151); + assert_ne!(application.password.to_owned(), SECRET.to_string()); + assert!(crypto::verify_password(SECRET.to_string(), application.password) .await .ok() .unwrap()); @@ -75,28 +66,20 @@ mod tests { async fn test_candidate_session_correct_password() { let db = &get_memory_sqlite_connection().await; - let candidate = ApplicationService::create_candidate_with_parent( - db, - 103151, - &"Tajny_kod".to_string(), - "".to_string(), - ) - .await - .unwrap() - .0; + let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap(); // correct password - let session = CandidateService::new_session( + let session = ApplicationService::new_session( db, - &candidate, - "Tajny_kod".to_string(), + &application, + SECRET.to_string(), "127.0.0.1".to_string(), ) .await .unwrap(); // println!("{}", session.err().unwrap().1); assert!( - CandidateService::auth(db, Uuid::parse_str(&session).unwrap()) + ApplicationService::auth(db, Uuid::parse_str(&session).unwrap()) .await .is_ok() ); @@ -106,20 +89,12 @@ mod tests { async fn test_candidate_session_incorrect_password() { let db = &get_memory_sqlite_connection().await; - let candidate_form = ApplicationService::create_candidate_with_parent( - &db, - 103151, - &"Tajny_kod".to_string(), - "".to_string(), - ) - .await - .unwrap() - .0; + let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap(); // incorrect password - assert!(CandidateService::new_session( + assert!(ApplicationService::new_session( db, - &candidate_form, + &application, "Spatny_kod".to_string(), "127.0.0.1".to_string() ) diff --git a/core/src/utils/db.rs b/core/src/utils/db.rs index ac1b4bd..7b7c779 100644 --- a/core/src/utils/db.rs +++ b/core/src/utils/db.rs @@ -1,4 +1,4 @@ -use entity::admin_session; +use entity::{admin_session, application}; use sea_orm::DbConn; use crate::Query; @@ -22,15 +22,17 @@ pub async fn get_memory_sqlite_connection() -> sea_orm::DbConn { 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 stmt2: TableCreateStatement = schema.create_table_from_entity(application::Entity); let stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity); - let stmt4: TableCreateStatement = schema.create_table_from_entity(parent::Entity); + let stmt4: TableCreateStatement = schema.create_table_from_entity(admin::Entity); let stmt5: TableCreateStatement = schema.create_table_from_entity(admin_session::Entity); + let stmt6: TableCreateStatement = schema.create_table_from_entity(parent::Entity); db.execute(db.get_database_backend().build(&stmt)).await.unwrap(); db.execute(db.get_database_backend().build(&stmt2)).await.unwrap(); db.execute(db.get_database_backend().build(&stmt3)).await.unwrap(); db.execute(db.get_database_backend().build(&stmt4)).await.unwrap(); db.execute(db.get_database_backend().build(&stmt5)).await.unwrap(); + db.execute(db.get_database_backend().build(&stmt6)).await.unwrap(); db } diff --git a/entity/src/application.rs b/entity/src/application.rs new file mode 100644 index 0000000..2067c8c --- /dev/null +++ b/entity/src/application.rs @@ -0,0 +1,45 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel)] +#[sea_orm(table_name = "application")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: i32, + pub candidate_id: i32, + pub password: String, + pub public_key: String, + pub private_key: String, + pub personal_id_number: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::candidate::Entity", + from = "Column::CandidateId", + to = "super::candidate::Column::Application", + on_update = "Cascade", + on_delete = "Cascade" + )] + Candidate, + #[sea_orm(has_many = "super::session::Entity")] + Session, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Candidate.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Session.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 5960976..96ccb92 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -5,9 +5,8 @@ use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] #[sea_orm(table_name = "candidate")] pub struct Model { - #[sea_orm(primary_key, auto_increment = false)] + #[sea_orm(primary_key)] pub application: i32, - pub code: String, pub name: Option, pub surname: Option, pub birth_surname: Option, @@ -22,23 +21,21 @@ pub struct Model { pub personal_identification_number: String, pub school_name: Option, pub health_insurance: Option, - pub public_key: String, - pub private_key: String, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(has_many = "super::session::Entity")] - Session, + #[sea_orm(has_many = "super::application::Entity")] + Application, #[sea_orm(has_many = "super::parent::Entity")] Parent, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::Session.def() + Relation::Application.def() } } diff --git a/entity/src/lib.rs b/entity/src/lib.rs index c2e3813..9904381 100644 --- a/entity/src/lib.rs +++ b/entity/src/lib.rs @@ -5,4 +5,5 @@ pub mod candidate; pub mod parent; pub mod session; pub mod admin_session; -pub mod session_trait; \ No newline at end of file +pub mod session_trait; +pub mod application; \ No newline at end of file diff --git a/entity/src/mod.rs b/entity/src/mod.rs index 268951e..43ca935 100644 --- a/entity/src/mod.rs +++ b/entity/src/mod.rs @@ -4,6 +4,7 @@ pub mod prelude; pub mod admin; pub mod admin_session; +pub mod application; pub mod candidate; pub mod parent; pub mod session; diff --git a/entity/src/prelude.rs b/entity/src/prelude.rs index 55797e7..c598a98 100644 --- a/entity/src/prelude.rs +++ b/entity/src/prelude.rs @@ -2,6 +2,7 @@ pub use super::admin::Entity as Admin; pub use super::admin_session::Entity as AdminSession; +pub use super::application::Entity as Application; pub use super::candidate::Entity as Candidate; pub use super::parent::Entity as Parent; pub use super::session::Entity as Session; diff --git a/entity/src/session.rs b/entity/src/session.rs index 63efcde..97ddaa8 100644 --- a/entity/src/session.rs +++ b/entity/src/session.rs @@ -19,18 +19,18 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm( - belongs_to = "super::candidate::Entity", + belongs_to = "super::application::Entity", from = "Column::CandidateId", - to = "super::candidate::Column::Application", + to = "super::application::Column::Id", on_update = "Cascade", on_delete = "Cascade" )] - Candidate, + Application, } -impl Related for Entity { +impl Related for Entity { fn to() -> RelationDef { - Relation::Candidate.def() + Relation::Application.def() } } diff --git a/migration/src/lib.rs b/migration/src/lib.rs index 8b2f658..13315cf 100644 --- a/migration/src/lib.rs +++ b/migration/src/lib.rs @@ -9,6 +9,8 @@ mod m20221027_194728_session_create_user_fk; mod m20221028_194728_session_create_admin_fk; mod m20221112_112212_create_parent_candidate_fk; mod m20221221_162232_create_admin_session; +mod m20230114_114628_create_application; +mod m20230114_114826_create_application_candidate_fk; pub struct Migrator; #[async_trait::async_trait] @@ -20,6 +22,7 @@ impl MigratorTrait for Migrator { Box::new(m20221024_124701_create_parent::Migration), Box::new(m20221025_154422_create_session::Migration), Box::new(m20221221_162232_create_admin_session::Migration), + Box::new(m20230114_114628_create_application::Migration), ]; if cfg!(debug_assertions) || cfg!(test) { @@ -36,6 +39,7 @@ impl MigratorTrait for Migrator { migrations.push(Box::new( m20221028_194728_session_create_admin_fk::Migration, )); + migrations.push(Box::new(m20230114_114826_create_application_candidate_fk::Migration)); } migrations diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index 5ee04bf..38532b6 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -16,9 +16,8 @@ impl MigrationTrait for Migration { .integer() .not_null() .primary_key() - .unique_key(), + .auto_increment(), ) - .col(ColumnDef::new(Candidate::Code).string().not_null()) .col(ColumnDef::new(Candidate::Name).string()) .col(ColumnDef::new(Candidate::Surname).string()) .col(ColumnDef::new(Candidate::BirthSurname).string()) @@ -33,8 +32,6 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string().not_null()) .col(ColumnDef::new(Candidate::SchoolName).string()) .col(ColumnDef::new(Candidate::HealthInsurance).string()) - .col(ColumnDef::new(Candidate::PublicKey).string().not_null()) - .col(ColumnDef::new(Candidate::PrivateKey).string().not_null()) .col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null()) .col(ColumnDef::new(Candidate::UpdatedAt).date_time().not_null()) .to_owned(), @@ -53,7 +50,6 @@ impl MigrationTrait for Migration { pub enum Candidate { Table, Application, - Code, Name, Surname, BirthSurname, @@ -68,8 +64,6 @@ pub enum Candidate { PersonalIdentificationNumber, SchoolName, HealthInsurance, - PublicKey, - PrivateKey, CreatedAt, UpdatedAt, } diff --git a/migration/src/m20221027_194728_session_create_user_fk.rs b/migration/src/m20221027_194728_session_create_user_fk.rs index 9a06760..309cdcd 100644 --- a/migration/src/m20221027_194728_session_create_user_fk.rs +++ b/migration/src/m20221027_194728_session_create_user_fk.rs @@ -1,6 +1,6 @@ use sea_orm_migration::prelude::*; -use crate::{m20221025_154422_create_session::Session, m20221024_121621_create_candidate::Candidate}; +use crate::{m20221025_154422_create_session::Session, m20230114_114628_create_application::Application}; #[derive(DeriveMigrationName)] pub struct Migration; @@ -11,7 +11,7 @@ impl MigrationTrait for Migration { manager.create_foreign_key(ForeignKey::create() .name("user_fk") .from(Session::Table, Session::CandidateId) - .to(Candidate::Table, Candidate::Application) + .to(Application::Table, Application::Id) .on_delete(ForeignKeyAction::Cascade) .on_update(ForeignKeyAction::Cascade) .to_owned()).await diff --git a/migration/src/m20230114_114628_create_application.rs b/migration/src/m20230114_114628_create_application.rs new file mode 100644 index 0000000..48330ab --- /dev/null +++ b/migration/src/m20230114_114628_create_application.rs @@ -0,0 +1,51 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Application::Table) + .if_not_exists() + .col( + ColumnDef::new(Application::Id) + .integer() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Application::CandidateId).integer().not_null()) + .col(ColumnDef::new(Application::Password).string().not_null()) + .col(ColumnDef::new(Application::PublicKey).string().not_null()) + .col(ColumnDef::new(Application::PrivateKey).string().not_null()) + .col(ColumnDef::new(Application::PersonalIdNumber).string().not_null()) + .col(ColumnDef::new(Application::CreatedAt).date_time().not_null()) + .col(ColumnDef::new(Application::UpdatedAt).date_time().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Application::Table).to_owned()) + .await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +pub enum Application { + Table, + Id, + Password, + PersonalIdNumber, + PublicKey, + PrivateKey, + CandidateId, + CreatedAt, + UpdatedAt, +} diff --git a/migration/src/m20230114_114826_create_application_candidate_fk.rs b/migration/src/m20230114_114826_create_application_candidate_fk.rs new file mode 100644 index 0000000..b92dcf6 --- /dev/null +++ b/migration/src/m20230114_114826_create_application_candidate_fk.rs @@ -0,0 +1,26 @@ +use sea_orm_migration::prelude::*; + +use crate::{m20221024_121621_create_candidate::Candidate, m20230114_114628_create_application::Application}; + +#[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(Application::Table, Application::CandidateId) + .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(Application::Table) + .to_owned()).await + } +}