Merge pull request #173 from EETagent/portfolio_reencrypt

Portfolio Reencrypt
This commit is contained in:
Vojtěch Jungmann 2023-01-22 11:38:52 +01:00 committed by GitHub
commit 6c93e673fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 31 deletions

View file

@ -200,6 +200,19 @@ pub fn create_identity() -> (String, String) {
)
}
pub async fn encrypt_buffer_with_recipients(
input_buffer: &[u8],
recipients: &Vec<String>,
) -> Result<Vec<u8>, 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<W: tokio::io::AsyncWrite + Unpin>(
input_buffer: &[u8],
output_buffer: &mut W,

View file

@ -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();

View file

@ -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();

View file

@ -321,7 +321,7 @@ impl From<&parent::Model> for EncryptedParentDetails {
impl EncryptedApplicationDetails {
pub async fn new(
form: &ApplicationDetails,
recipients: Vec<String>,
recipients: &Vec<String>,
) -> Result<EncryptedApplicationDetails, ServiceError> {
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();

View file

@ -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"];
@ -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,25 +327,19 @@ impl ApplicationService {
recipients.append(&mut admin_public_keys);
recipients.append(&mut applications.iter().map(|a| a.public_key.to_owned()).collect());
let dec_details = EncryptedApplicationDetails::from((&candidate, &parents))
.decrypt(admin_private_key).await?;
let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?;
let candidate = Mutation::update_personal_id(db,
candidate,
&enc_details.candidate.personal_id_number.to_owned()
.ok_or(ServiceError::CandidateDetailsNotSet)?.to_string()
let candidate = Self::update_all_application_details(db,
application.id,
candidate,
&recipients,
&admin_private_key
).await?;
Mutation::update_candidate_opt_details(db,
candidate,
enc_details.candidate,
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?;
if PortfolioService::get_submission_progress(candidate.id).await? == SubmissionProgress::Submitted {
PortfolioService::reencrypt_portfolio(
candidate.id,
admin_private_key,
&recipients
).await?;
}
Ok(
@ -359,6 +350,37 @@ impl ApplicationService {
}
)
}
async fn update_all_application_details(db: &DbConn,
application_id: i32,
candidate: candidate::Model,
recipients: &Vec<String>,
admin_private_key: &String
) -> Result<candidate::Model, ServiceError> {
let parents = Query::find_candidate_parents(db, &candidate).await?;
let dec_details = EncryptedApplicationDetails::from((&candidate, &parents))
.decrypt(admin_private_key.to_owned()).await?;
let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?;
let candidate = Mutation::update_personal_id(db,
candidate,
&enc_details.candidate.personal_id_number.to_owned()
.ok_or(ServiceError::CandidateDetailsNotSet)?.to_string()
).await?;
let candidate = Mutation::update_candidate_opt_details(db,
candidate,
enc_details.candidate,
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(candidate)
}
}
#[async_trait]
@ -457,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;

View file

@ -368,15 +368,46 @@ impl PortfolioService {
/// Returns decrypted portfolio zip as Vec of bytes
pub async fn get_portfolio(candidate_id: i32, private_key: String) -> Result<Vec<u8>, 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<String>
) -> 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?;
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);
Ok(())
}
}
#[cfg(test)]