From e7f97ebd2cc377abb72087d0461b722dfd3021fb Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Sun, 6 Nov 2022 14:43:48 +0100 Subject: [PATCH] feat: decrypt user details --- core/src/error.rs | 4 + core/src/services/candidate_service.rs | 169 ++++++++++++++++++++++--- 2 files changed, 157 insertions(+), 16 deletions(-) diff --git a/core/src/error.rs b/core/src/error.rs index 5fb8cfc..428b0f9 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -11,6 +11,8 @@ pub enum ServiceError { UserNotFoundBySessionId, CryptoHashFailed, CryptoEncryptFailed, + CryptoDecryptFailed, + CandidateDetailsNotSet, } impl ServiceError { @@ -28,6 +30,8 @@ impl ServiceError { ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()), ServiceError::CryptoHashFailed => (500, "Crypto hash failed, please contact technical support".to_string()), ServiceError::CryptoEncryptFailed => (500, "Crypto encryption failed, please contact technical support".to_string()), + ServiceError::CryptoDecryptFailed => (500, "Crypto decryption failed, please contact technical support".to_string()), + ServiceError::CandidateDetailsNotSet => (500, "Candidate details not set, please contact technical support".to_string()), } } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index c8d0d7b..4f2baa7 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,4 +1,3 @@ -use chrono::NaiveDate; use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; use serde::Deserialize; @@ -67,6 +66,107 @@ impl EncryptedAddUserData { study, } } + + fn extract_enc_candidate_details(candidate: candidate::Model) -> Result { + let ( // TODO: simplify?? + Some(name), + Some(surname), + Some(birthplace), + // Some(birthdate), + Some(address), + Some(telephone), + Some(citizenship), + Some(email), + Some(sex), + Some(study), + ) = ( + candidate.name, + candidate.surname, + candidate.birthplace, + // candidate.birthdate, + candidate.address, + candidate.telephone, + candidate.citizenship, + candidate.email, + candidate.sex, + candidate.study, + ) else { + return Err(ServiceError::CandidateDetailsNotSet); + }; + + Ok(AddUserDetailsForm { + name, + surname, + birthplace, + // birthdate, + address, + telephone, + citizenship, + email, + sex, + study, + }) + } + + pub fn from_model(candidate: candidate::Model) -> Result { + let Ok(details) = Self::extract_enc_candidate_details(candidate) else { + return Err(ServiceError::CandidateDetailsNotSet); + }; + Ok(EncryptedAddUserData { + name: details.name, + surname: details.surname, + birthplace: details.birthplace, + // birthdate, + address: details.address, + telephone: details.telephone, + citizenship: details.citizenship, + email: details.email, + sex: details.sex, + study: details.study, + }) + } + + pub async fn decrypt(self, priv_key: String) -> Result { + let ( + Ok(name), + Ok(surname), + Ok(birthplace), + // Ok(enc_birthdate), + Ok(address), + Ok(telephone), + Ok(citizenship), + Ok(email), + Ok(sex), + Ok(study), + ) = tokio::join!( + crypto::decrypt_password_with_private_key(&self.name, &priv_key), + crypto::decrypt_password_with_private_key(&self.surname, &priv_key), + crypto::decrypt_password_with_private_key(&self.birthplace, &priv_key), + crypto::decrypt_password_with_private_key(&self.address, &priv_key), + crypto::decrypt_password_with_private_key(&self.telephone, &priv_key), + crypto::decrypt_password_with_private_key(&self.citizenship, &priv_key), + crypto::decrypt_password_with_private_key(&self.email, &priv_key), + crypto::decrypt_password_with_private_key(&self.sex, &priv_key), + crypto::decrypt_password_with_private_key(&self.study, &priv_key), + ) else { + panic!("Failed to encrypt user details"); // TODO + }; + + Ok( + AddUserDetailsForm { + name, + surname, + birthplace, + // birthdate: NaiveDate::from_ymd(2000, 1, 1), + address, + telephone, + citizenship, + email, + sex, + study, + } + ) + } } #[derive(Debug, Deserialize)] @@ -146,14 +246,6 @@ impl CandidateService { user: candidate::Model, form: AddUserDetailsForm, ) -> Result { - /* let Ok(user) = Query::find_candidate_by_id(db, application_id).await else { - return Err(ServiceError::DbError); - }; - - let Some(user_unwrapped) = user else { - return Err(ServiceError::UserNotFound); - }; */ - let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { return Err(ServiceError::DbError); }; @@ -176,6 +268,32 @@ impl CandidateService { .map_err(|_| ServiceError::DbError) } + pub async fn decrypt_details( + db: &DbConn, + candidate_id: i32, + password: String, + ) -> Result { + // compare passwords // TODO: login in api?? // TODO: dedicated function + let candidate = Query::find_candidate_by_id(db, candidate_id) + .await + .map_err(|_| ServiceError::DbError)? + .ok_or(ServiceError::UserNotFound)?; + + match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { + Ok(valid) => { + if !valid { + return Err(ServiceError::InvalidCredentials) + } + }, + Err(_) => {return Err(ServiceError::InvalidCredentials)} + } + + let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password).await.ok().unwrap(); + let enc_details = EncryptedAddUserData::from_model(candidate)?; + + enc_details.decrypt(dec_priv_key).await + } + pub async fn login( db: &DbConn, user_id: i32, @@ -208,11 +326,13 @@ impl CandidateService { #[cfg(test)] mod tests { - use chrono::NaiveDate; use sea_orm::{Database, DbConn}; + use entity::candidate::Model; use crate::{crypto, services::candidate_service::{CandidateService, AddUserDetailsForm}}; + use super::EncryptedAddUserData; + #[tokio::test] async fn test_application_id_validation() { assert!(CandidateService::is_application_id_valid(101_101)); @@ -278,9 +398,9 @@ mod tests { assert_eq!(secret_message, decrypted_message); } - #[tokio::test] - async fn test_put_user_data() { - let db = get_memory_sqlite_connection().await; + + #[cfg(test)] + async fn put_user_data(db: &DbConn) -> Model { let plain_text_password = "test".to_string(); let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) .await @@ -299,10 +419,27 @@ mod tests { sex: "test".to_string(), study: "test".to_string(), }; - let candidate = CandidateService::add_user_details(&db, candidate, form).await.ok().unwrap(); - - + CandidateService::add_user_details(&db, candidate, form).await.ok().unwrap() + } + #[tokio::test] + async fn test_put_user_data() { + let db = get_memory_sqlite_connection().await; + let candidate = put_user_data(&db).await; assert!(candidate.name.is_some()); } + + #[tokio::test] + async fn test_encrypt_decrypt_user_data() { + let password = "test".to_string(); + let db = get_memory_sqlite_connection().await; + let enc_candidate = put_user_data(&db).await; + + let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) + .await + .unwrap(); + let dec_candidate = EncryptedAddUserData::from_model(enc_candidate).unwrap().decrypt(dec_priv_key).await.unwrap(); + + assert_eq!(dec_candidate.name, "test"); + } }