From 85c6a47232b0bd362b2a0d12faf5c83c080cddee Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 14 Jan 2023 17:33:40 +0100 Subject: [PATCH] feat!: lock candidate details for application entities. who can't decrypt it --- api/src/routes/admin.rs | 7 +++---- api/src/routes/candidate.rs | 16 ++++++++++------ core/src/database/mutation/candidate.rs | 10 ++++++---- core/src/database/query/application.rs | 2 +- core/src/error.rs | 5 ++++- core/src/services/application_service.rs | 17 ++++++++++++----- core/src/services/candidate_service.rs | 12 +++++++++--- entity/src/candidate.rs | 1 + .../src/m20221024_121621_create_candidate.rs | 2 ++ 9 files changed, 48 insertions(+), 24 deletions(-) diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index b92a97f..f09934c 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -166,13 +166,12 @@ pub async fn get_candidate( .await .map_err(|e| to_custom_error(ServiceError::DbError(e)))? .ok_or(to_custom_error(ServiceError::CandidateNotFound))?; - let candidate = ApplicationService::find_related_candidate(db, application).await - .map_err(to_custom_error)?; let details = ApplicationService::decrypt_all_details( private_key, db, - candidate + &application, + false, ) .await .map_err(to_custom_error)?; @@ -194,7 +193,7 @@ pub async fn delete_candidate( .await .map_err(|e| to_custom_error(ServiceError::DbError(e)))? .ok_or(to_custom_error(ServiceError::CandidateNotFound))?; - let candidate = ApplicationService::find_related_candidate(db, application).await.map_err(to_custom_error)?; + let candidate = ApplicationService::find_related_candidate(db, &application).await.map_err(to_custom_error)?; // TODO diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index dbe69c2..832b704 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -77,7 +77,7 @@ pub async fn whoami(conn: Connection<'_, Db>, session: ApplicationAuth) -> Resul let private_key = session.get_private_key(); let application: entity::application::Model = session.into(); - let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO + let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO println!("candidate: {:?}", candidate); let response = NewCandidateResponse::from_encrypted(&private_key, candidate).await .map_err(to_custom_error)?; @@ -95,9 +95,9 @@ pub async fn post_details( let db = conn.into_inner(); let form = details.into_inner(); let application: application::Model = session.into(); - let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO + let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO - let _candidate_parent = ApplicationService::add_all_details(db, &application.public_key, candidate, &form) + let _candidate_parent = ApplicationService::add_all_details(db, &application, candidate, &form) .await .map_err(to_custom_error)?; @@ -112,9 +112,13 @@ pub async fn get_details( let db = conn.into_inner(); let private_key = session.get_private_key(); 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) + let details = ApplicationService::decrypt_all_details( + private_key, + db, + &application, + true + ) .await .map(|x| Json(x)) .map_err(to_custom_error); @@ -218,7 +222,7 @@ pub async fn submit_portfolio( let db = conn.into_inner(); let application: entity::application::Model = session.into(); - let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO + let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO let submit = PortfolioService::submit(&application.public_key, &candidate, &db).await; diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index 2f964a8..1994b82 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -44,11 +44,12 @@ impl Mutation { pub async fn update_candidate_details( db: &DbConn, - user: candidate::Model, + candidate: candidate::Model, enc_candidate: EncryptedCandidateDetails, + encrypted_by_id: i32, ) -> Result { - let application = user.id; - let mut candidate: candidate::ActiveModel = user.into(); + let application = candidate.id; + let mut candidate: candidate::ActiveModel = candidate.into(); candidate.name = Set(enc_candidate.name.map(|e| e.into())); candidate.surname = Set(enc_candidate.surname.map(|e| e.into())); @@ -63,6 +64,7 @@ impl Mutation { candidate.school_name = Set(enc_candidate.school_name.map(|e| e.into())); candidate.health_insurance = Set(enc_candidate.health_insurance.map(|e| e.into())); candidate.study = Set(enc_candidate.study.map(|e| e.into())); + candidate.encrypted_by_id = Set(Some(encrypted_by_id)); candidate.updated_at = Set(chrono::offset::Local::now().naive_local()); @@ -128,7 +130,7 @@ mod tests { vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], ).await.unwrap(); - let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap(); + let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate, 1).await.unwrap(); let candidate = Query::find_candidate_by_id(&db, candidate.id) .await diff --git a/core/src/database/query/application.rs b/core/src/database/query/application.rs index 54974f7..2ec4077 100644 --- a/core/src/database/query/application.rs +++ b/core/src/database/query/application.rs @@ -27,7 +27,7 @@ impl Query { pub async fn find_related_candidate( db: &DbConn, - application: application::Model, + application: &application::Model, ) -> Result, DbErr> { application .find_related(candidate::Entity) diff --git a/core/src/error.rs b/core/src/error.rs index 72bbca0..adecdfb 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -18,6 +18,8 @@ pub enum ServiceError { UserAlreadyExists, #[error("Candidate not found")] CandidateNotFound, + #[error("Resource is locked")] + Locked, #[error("Parrent not found")] ParentNotFound, #[error("Database error")] @@ -69,7 +71,7 @@ pub enum ServiceError { impl ServiceError { pub fn code(&self) -> u16 { match self { - // 40X + // 4XX ServiceError::InvalidApplicationId => 400, ServiceError::ParentOverflow => 400, ServiceError::Unauthorized => 401, @@ -79,6 +81,7 @@ impl ServiceError { ServiceError::CandidateNotFound => 404, ServiceError::IncompletePortfolio => 406, ServiceError::UserAlreadyExists => 409, + ServiceError::Locked => 423, // 500 ServiceError::ParentNotFound => 500, ServiceError::DbError(_) => 500, diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 02a550f..c1a7570 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -160,7 +160,7 @@ impl ApplicationService { pub async fn find_related_candidate( db: &DbConn, - application: application::Model, + application: &application::Model, ) -> Result { let candidate = Query::find_related_candidate(db, application).await?; if let Some(candidate) = candidate { @@ -172,13 +172,13 @@ impl ApplicationService { pub async fn add_all_details( db: &DbConn, - public_key: &String, + application: &application::Model, candidate: candidate::Model, form: &ApplicationDetails, ) -> Result<(candidate::Model, Vec), ServiceError> { - let recipients = get_recipients(db, public_key).await?; - let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients).await?; + let recipients = get_recipients(db, &application.public_key).await?; + let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients, application.id).await?; let parents = ParentService::add_parents_details(db, &candidate, &form.parents, &recipients).await?; Ok( ( @@ -191,8 +191,15 @@ impl ApplicationService { pub async fn decrypt_all_details( private_key: String, db: &DbConn, - candidate: candidate::Model, + application: &application::Model, + restrict_access: bool, ) -> Result { + let candidate = ApplicationService::find_related_candidate(db, application).await?; + + if restrict_access && candidate.encrypted_by_id.is_some() && candidate.encrypted_by_id != Some(application.id) { + return Err(ServiceError::Locked) + } + let parents = Query::find_candidate_parents(db, &candidate).await?; let enc_details = EncryptedApplicationDetails::from((&candidate, parents)); diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 2641786..ab0608e 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -48,9 +48,15 @@ impl CandidateService { candidate: candidate::Model, details: &CandidateDetails, recipients: &Vec, + encrypted_by: i32, ) -> Result { let enc_details = EncryptedCandidateDetails::new(&details, recipients).await?; - let model = Mutation::update_candidate_details(db, candidate, enc_details).await?; + let model = Mutation::update_candidate_details( + db, + candidate, + enc_details, + encrypted_by + ).await?; Ok(model) } @@ -148,11 +154,11 @@ pub mod tests { "0000001111".to_string() ).await.unwrap(); - let candidate= ApplicationService::find_related_candidate(db, application.to_owned()).await.unwrap(); + let candidate= ApplicationService::find_related_candidate(db, &application).await.unwrap(); let form = APPLICATION_DETAILS.lock().unwrap().clone(); - let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form) + let (candidate, parents) = ApplicationService::add_all_details(&db, &application, candidate, &form) .await .unwrap(); diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 79f96bc..c1a1d77 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -21,6 +21,7 @@ pub struct Model { pub personal_identification_number: String, pub school_name: Option, pub health_insurance: Option, + pub encrypted_by_id: Option, pub created_at: DateTime, pub updated_at: DateTime, } diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index c822cd4..0b4f77b 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -32,6 +32,7 @@ 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::EncryptedById).integer()) .col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null()) .col(ColumnDef::new(Candidate::UpdatedAt).date_time().not_null()) .to_owned(), @@ -64,6 +65,7 @@ pub enum Candidate { PersonalIdentificationNumber, SchoolName, HealthInsurance, + EncryptedById, CreatedAt, UpdatedAt, }