From c402d6f4b955bb1ba3d815d41486ae4aadd2c912 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 20 Jan 2023 15:32:10 +0100 Subject: [PATCH 1/3] feat: portfolio reencrypt --- core/src/crypto.rs | 13 ++++++ core/src/database/mutation/candidate.rs | 2 +- core/src/database/mutation/parent.rs | 2 +- core/src/models/candidate_details.rs | 6 +-- core/src/services/application_service.rs | 50 +++++++++++++++++------- core/src/services/portfolio_service.rs | 36 +++++++++++++++-- 6 files changed, 86 insertions(+), 23 deletions(-) diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 05e2644..1444f59 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -200,6 +200,19 @@ pub fn create_identity() -> (String, String) { ) } +pub async fn encrypt_buffer_with_recipients( + input_buffer: &[u8], + recipients: &Vec, +) -> Result, ServiceError> { + let mut output_buffer = vec![]; + age_encrypt_with_recipients(input_buffer, + &mut output_buffer, + &recipients.iter().map(|s| s.as_str()).collect() + ).await?; + + Ok(output_buffer) +} + async fn age_encrypt_with_recipients( input_buffer: &[u8], output_buffer: &mut W, diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index e5bbdc0..e8e723c 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -122,7 +122,7 @@ mod tests { let encrypted_details: EncryptedApplicationDetails = EncryptedApplicationDetails::new( &APPLICATION_DETAILS.lock().unwrap().clone(), - vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], + &vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], ).await.unwrap(); let candidate = Mutation::update_candidate_opt_details(&db, candidate, encrypted_details.candidate, 1).await.unwrap(); diff --git a/core/src/database/mutation/parent.rs b/core/src/database/mutation/parent.rs index 6959cb6..ee207d6 100644 --- a/core/src/database/mutation/parent.rs +++ b/core/src/database/mutation/parent.rs @@ -77,7 +77,7 @@ mod tests { let encrypted_details: EncryptedApplicationDetails = EncryptedApplicationDetails::new( &APPLICATION_DETAILS.lock().unwrap().clone(), - vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], + &vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()], ) .await .unwrap(); diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index 836e903..3e2dcc4 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -321,7 +321,7 @@ impl From<&parent::Model> for EncryptedParentDetails { impl EncryptedApplicationDetails { pub async fn new( form: &ApplicationDetails, - recipients: Vec, + recipients: &Vec, ) -> Result { let candidate = EncryptedCandidateDetails::new(&form.candidate, &recipients).await?; let enc_parents = future::try_join_all( @@ -459,7 +459,7 @@ pub mod tests { async fn test_encrypted_application_details_new() { let encrypted_details = EncryptedApplicationDetails::new( &APPLICATION_DETAILS.lock().unwrap().clone(), - vec![PUBLIC_KEY.to_string()], + &vec![PUBLIC_KEY.to_string()], ) .await .unwrap(); @@ -488,7 +488,7 @@ pub mod tests { async fn test_encrypted_application_details_decrypt() { let encrypted_details = EncryptedApplicationDetails::new( &APPLICATION_DETAILS.lock().unwrap().clone(), - vec![PUBLIC_KEY.to_string()], + &vec![PUBLIC_KEY.to_string()], ) .await .unwrap(); diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 991184c..8d8b959 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -265,8 +265,6 @@ impl ApplicationService { ).await }) ).await - - } async fn decrypt_private_key( @@ -291,7 +289,6 @@ impl ApplicationService { } } - // TODO pub async fn reset_password( admin_private_key: String, db: &DbConn, @@ -300,7 +297,6 @@ impl ApplicationService { let application = Query::find_application_by_id(db, id).await? .ok_or(ServiceError::CandidateNotFound)?; let candidate = ApplicationService::find_related_candidate(db, &application).await?; - 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?; @@ -319,6 +315,7 @@ impl ApplicationService { 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(application.personal_id_number.clone()) .decrypt(&admin_private_key) @@ -330,8 +327,37 @@ impl ApplicationService { recipients.append(&mut admin_public_keys); recipients.append(&mut applications.iter().map(|a| a.public_key.to_owned()).collect()); + let candidate = Self::update_all_application_details(db, + application.id, + candidate, + &recipients, + &admin_private_key + ).await?; + + PortfolioService::reencrypt_portfolio( + candidate.id, + admin_private_key, + &recipients + ).await?; + + Ok( + CreateCandidateResponse { + application_id: id, + personal_id_number, + password: new_password_plain, + } + ) + } + + async fn update_all_application_details(db: &DbConn, + application_id: i32, + candidate: candidate::Model, + recipients: &Vec, + admin_private_key: &String + ) -> Result { + let parents = Query::find_candidate_parents(db, &candidate).await?; let dec_details = EncryptedApplicationDetails::from((&candidate, &parents)) - .decrypt(admin_private_key).await?; + .decrypt(admin_private_key.to_owned()).await?; let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?; @@ -341,23 +367,17 @@ impl ApplicationService { .ok_or(ServiceError::CandidateDetailsNotSet)?.to_string() ).await?; - Mutation::update_candidate_opt_details(db, + let candidate = Mutation::update_candidate_opt_details(db, candidate, enc_details.candidate, - application.id + application_id ).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, - } - ) + + Ok(candidate) } } diff --git a/core/src/services/portfolio_service.rs b/core/src/services/portfolio_service.rs index 19cede7..e05d0aa 100644 --- a/core/src/services/portfolio_service.rs +++ b/core/src/services/portfolio_service.rs @@ -368,15 +368,45 @@ impl PortfolioService { /// Returns decrypted portfolio zip as Vec of bytes pub async fn get_portfolio(candidate_id: i32, private_key: String) -> Result, ServiceError> { info!("PORTFOLIO {} DECRYPT STARTED", candidate_id); - let path = Self::get_file_store_path().join(&candidate_id.to_string()).to_path_buf(); - - let path = path.join(FileType::Age.as_str()); + let path = Self::get_file_store_path() + .join(&candidate_id.to_string()) + .join(FileType::Age.as_str()) + .to_path_buf(); let buffer = crypto::decrypt_file_with_private_key_as_buffer(path, &private_key).await?; info!("PORTFOLIO {} DECRYPT FINISHED", candidate_id); Ok(buffer) } + + pub async fn reencrypt_portfolio(candidate_id: i32, + private_key: String, + recipients: &Vec + ) -> Result<(), ServiceError> { + info!("PORTFOLIO {} REENCRYPT STARTED", candidate_id); + let path = Self::get_file_store_path() + .join(&candidate_id.to_string()) + .join(FileType::Age.as_str()) + .to_path_buf(); + + let plain_portfolio = crypto::decrypt_file_with_private_key_as_buffer( + path.to_owned(), + &private_key + ).await?; + + tokio::fs::remove_file(path.to_owned()).await?; + + let enc_portfolio= crypto::encrypt_buffer_with_recipients( + &plain_portfolio, + recipients + ).await?; + + tokio::fs::write(path, enc_portfolio).await?; + + info!("PORTFOLIO {} REENCRYPT FINISHED", candidate_id); + + Ok(()) + } } #[cfg(test)] From dd456a7870919ebcd6684906801e354da327b701 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 20 Jan 2023 17:00:26 +0100 Subject: [PATCH 2/3] fix: password reset when portfolio not submitted --- core/src/services/application_service.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 8d8b959..1217fe7 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -6,7 +6,7 @@ use sea_orm::{DbConn, prelude::Uuid, IntoActiveModel}; use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::EncryptedApplicationDetails, models::{candidate::{ApplicationDetails, CreateCandidateResponse}, candidate_details::{EncryptedString, EncryptedCandidateDetails}, auth::AuthenticableTrait, application::ApplicationResponse}, Mutation, crypto::{hash_password, self}}; -use super::{parent_service::ParentService, candidate_service::CandidateService, session_service::SessionService, portfolio_service::PortfolioService}; +use super::{parent_service::ParentService, candidate_service::CandidateService, session_service::SessionService, portfolio_service::{PortfolioService, SubmissionProgress}}; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; @@ -334,11 +334,13 @@ impl ApplicationService { &admin_private_key ).await?; - PortfolioService::reencrypt_portfolio( - candidate.id, - admin_private_key, - &recipients - ).await?; + if PortfolioService::get_submission_progress(candidate.id).await? == SubmissionProgress::Submitted { + PortfolioService::reencrypt_portfolio( + candidate.id, + admin_private_key, + &recipients + ).await?; + } Ok( CreateCandidateResponse { @@ -477,7 +479,6 @@ mod application_tests { assert!(!ApplicationService::is_application_id_valid(101)); } - // TODO #[tokio::test] async fn test_password_reset() { let db = get_memory_sqlite_connection().await; From 205996cc2e6fec0bb772125cf0524ecb57f4e570 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sat, 21 Jan 2023 23:28:35 +0100 Subject: [PATCH 3/3] refactor: remove portfolio file after encrypting --- core/src/services/portfolio_service.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/services/portfolio_service.rs b/core/src/services/portfolio_service.rs index e05d0aa..1091afb 100644 --- a/core/src/services/portfolio_service.rs +++ b/core/src/services/portfolio_service.rs @@ -394,13 +394,14 @@ impl PortfolioService { &private_key ).await?; - tokio::fs::remove_file(path.to_owned()).await?; - + let enc_portfolio= crypto::encrypt_buffer_with_recipients( &plain_portfolio, recipients ).await?; - + + tokio::fs::remove_file(path.to_owned()).await?; + tokio::fs::write(path, enc_portfolio).await?; info!("PORTFOLIO {} REENCRYPT FINISHED", candidate_id);