From 4a8c520cae975b4488230c3d2ffba06d43d52035 Mon Sep 17 00:00:00 2001 From: EETagent Date: Thu, 10 Nov 2022 23:16:34 +0100 Subject: [PATCH 01/10] feat: implement folder & file creation --- core/src/services/candidate_service.rs | 30 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index dd5bffb..66df9e3 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,5 +1,8 @@ +use std::path::Path; + use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; +use tokio::io::AsyncWriteExt; use crate::{ candidate_details::EncryptedApplicationDetails, @@ -54,6 +57,10 @@ impl CandidateService { return Err(ServiceError::CryptoHashFailed); }; + // TODO: Specify root path in config? + tokio::fs::create_dir_all(Path::new(&application_id.to_string()).join("cache")).await?; + + let candidate = Mutation::create_candidate( db, application_id, @@ -88,22 +95,33 @@ impl CandidateService { && candidate.study.is_some() } - pub async fn add_cover_letter(candidate_id: i32, letter: Vec) -> Result<(), ServiceError> { - // TODO + async fn write_portfolio_file( + candidate_id: i32, + data: Vec, + filename: &str, + ) -> Result<(), ServiceError> { + let cache_path = Path::new(&candidate_id.to_string()).join("cache"); + + let mut file = tokio::fs::File::create(cache_path.join(filename)).await?; + + file.write_all(&data).await?; + Ok(()) } + pub async fn add_cover_letter(candidate_id: i32, letter: Vec) -> Result<(), ServiceError> { + Self::write_portfolio_file(candidate_id, letter, "MOTIVACNI_DOPIS.pdf").await + } + pub async fn add_portfolio_letter( candidate_id: i32, letter: Vec, ) -> Result<(), ServiceError> { - // TODO - Ok(()) + Self::write_portfolio_file(candidate_id, letter, "PORTFOLIO.pdf").await } pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec) -> Result<(), ServiceError> { - // TODO - Ok(()) + Self::write_portfolio_file(candidate_id, zip, "PORTFOLIO.zip").await } async fn decrypt_private_key( From 91125ce7f1f1fbfe8f0d459bd8c2647dc4c38a2e Mon Sep 17 00:00:00 2001 From: EETagent Date: Thu, 10 Nov 2022 23:22:03 +0100 Subject: [PATCH 02/10] feat: check if portfolio is complete --- core/src/services/candidate_service.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 66df9e3..9d53d16 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -124,6 +124,20 @@ impl CandidateService { Self::write_portfolio_file(candidate_id, zip, "PORTFOLIO.zip").await } + pub async fn is_portfolio_complete(candidate_id: i32) -> bool { + let cache_path = Path::new(&candidate_id.to_string()).join("cache"); + + tokio::fs::metadata(cache_path.join("MOTIVACNI_DOPIS.pdf")) + .await + .is_ok() + && tokio::fs::metadata(cache_path.join("PORTFOLIO.pdf")) + .await + .is_ok() + && tokio::fs::metadata(cache_path.join("PORTFOLIO.zip")) + .await + .is_ok() + } + async fn decrypt_private_key( candidate: candidate::Model, password: String, From 836146eeb70bce8ef7d2e3d1d2294f84a2bd152b Mon Sep 17 00:00:00 2001 From: EETagent Date: Thu, 10 Nov 2022 23:52:32 +0100 Subject: [PATCH 03/10] feat: add async_zip --- Cargo.lock | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ core/Cargo.toml | 2 + 2 files changed, 159 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 8200336..91afc04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aead" version = "0.5.1" @@ -170,6 +176,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "bzip2", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "async-stream" version = "0.3.3" @@ -212,6 +235,29 @@ dependencies = [ "syn", ] +[[package]] +name = "async_io_utilities" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b20cffc5590f4bf33f05f97a3ea587feba9c50d20325b401daa096b92ff7da0" +dependencies = [ + "tokio", +] + +[[package]] +name = "async_zip" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a36d43bdefc7215b2b3a97edd03b1553b7969ad76551025eedd3b913c645f6e" +dependencies = [ + "async-compression", + "async_io_utilities", + "chrono", + "crc32fast", + "thiserror", + "tokio", +] + [[package]] name = "atoi" version = "1.0.0" @@ -326,11 +372,35 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cc" version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +dependencies = [ + "jobserver", +] [[package]] name = "cfb" @@ -536,6 +606,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-queue" version = "0.3.6" @@ -794,6 +873,16 @@ dependencies = [ "toml", ] +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fluent" version = "0.16.0" @@ -1373,6 +1462,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.60" @@ -1448,6 +1546,17 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1484,6 +1593,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.5" @@ -1896,6 +2014,7 @@ dependencies = [ "argon2", "async-compat", "async-tempfile", + "async_zip", "base64", "chrono", "dotenv", @@ -3552,6 +3671,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yansi" version = "0.5.1" @@ -3578,3 +3706,32 @@ dependencies = [ "syn", "synstructure", ] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.1+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +dependencies = [ + "cc", + "libc", +] diff --git a/core/Cargo.toml b/core/Cargo.toml index 6ebc7c8..bf6c2ac 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -27,6 +27,8 @@ async-compat = "^0.2" # file identifier infer = "^0.11" +async_zip = "0.0.9" + # crypto rand = "^0.8" aes-gcm-siv = { version = "^0.11", features = ["std"] } From c20f07e2681febe0a59738e4cdc1906aea4b9693 Mon Sep 17 00:00:00 2001 From: EETagent Date: Thu, 10 Nov 2022 23:52:52 +0100 Subject: [PATCH 04/10] feat: submit portfolio --- core/src/services/candidate_service.rs | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 9d53d16..965d80c 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -2,7 +2,7 @@ use std::path::Path; use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use crate::{ candidate_details::EncryptedApplicationDetails, @@ -138,6 +138,56 @@ impl CandidateService { .is_ok() } + pub async fn submit_portfolio(candidate_id: i32) -> Result<(), ServiceError> { + let path = Path::new(&candidate_id.to_string()).to_path_buf(); + let cache_path = path.join("cache"); + + if Self::is_portfolio_complete(candidate_id).await == false { + return Err(ServiceError::IncompletePortfolio); + } + + let archive = tokio::fs::File::create(path.join("PORTFOLIO.zip")).await; + + let Ok(mut archive) = archive else { + return Err(ServiceError::FileCreationError); + }; + + let mut writer = async_zip::write::ZipFileWriter::new(&mut archive); + + for entry in vec!["MOTIVACNI_DOPIS.pdf", "PORTFOLIO.pdf", "PORTFOLIO.zip"] { + let entry_file = tokio::fs::File::open(cache_path.join(entry)).await; + + let Ok(mut entry_file) = entry_file else { + return Err(ServiceError::FileOpenError); + }; + + let mut contents = vec![]; + let read = entry_file.read_to_end(&mut contents).await; + + let Ok(_) = read else { + return Err(ServiceError::FileReadError); + }; + + let builder = + async_zip::ZipEntryBuilder::new(entry.to_string(), async_zip::Compression::Deflate); + // TODO: Ne unwrap + let mut entry_writer = writer.write_entry_stream(builder).await.unwrap(); + + // TODO: write_all_buf? + let write = entry_writer.write_all(&mut contents).await; + + let Ok(_) = write else { + return Err(ServiceError::FileWriteError); + }; + } + + // TODO: Ne unwrap + writer.close().await.unwrap(); + archive.shutdown().await.unwrap(); + + Ok(()) + } + async fn decrypt_private_key( candidate: candidate::Model, password: String, From 6d386e90a4b8bb28ee1f2971fa7dcfcc7ef5abe1 Mon Sep 17 00:00:00 2001 From: EETagent Date: Fri, 11 Nov 2022 19:53:59 +0100 Subject: [PATCH 05/10] feat: portfolio encryption --- core/src/services/candidate_service.rs | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 965d80c..3b4e944 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -138,7 +138,7 @@ impl CandidateService { .is_ok() } - pub async fn submit_portfolio(candidate_id: i32) -> Result<(), ServiceError> { + pub async fn submit_portfolio(candidate_id: i32, db: &DbConn) -> Result<(), ServiceError> { let path = Path::new(&candidate_id.to_string()).to_path_buf(); let cache_path = path.join("cache"); @@ -185,6 +185,36 @@ impl CandidateService { writer.close().await.unwrap(); archive.shutdown().await.unwrap(); + let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { + return Err(ServiceError::DbError); + }; + + let Ok(candidate) = Query::find_candidate_by_id(db, candidate_id).await else { + return Err(ServiceError::DbError); + }; + + let Some(candidate) = candidate else { + return Err(ServiceError::UserNotFound); + }; + + 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]; + + recipients.append(&mut admin_public_keys_refrence); + + let Ok(_) = crypto::encrypt_file_with_recipients( + path.join("PORTFOLIO.zip"), + path.join("PORTFOLIO.zip"), + recipients, + ) + .await else { + return Err(ServiceError::CryptoEncryptFailed); + }; + Ok(()) } From c352dc0212b35a1345a0ed86bc482145c5924ce4 Mon Sep 17 00:00:00 2001 From: EETagent Date: Fri, 11 Nov 2022 19:56:30 +0100 Subject: [PATCH 06/10] feat: decrypt portfolio --- core/src/services/candidate_service.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 3b4e944..98dcb26 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -218,6 +218,26 @@ impl CandidateService { Ok(()) } + pub async fn get_portfolio(candidate_id: i32, db: &DbConn) -> Result, ServiceError> { + let Ok(candidate) = Query::find_candidate_by_id(db, candidate_id).await else { + return Err(ServiceError::DbError); + }; + + let Some(candidate) = candidate else { + return Err(ServiceError::UserNotFound); + }; + + let candidate_public_key = candidate.public_key; + + let path = Path::new(&candidate_id.to_string()).join("PORTFOLIO.zip"); + + let Ok(buffer) = crypto::decrypt_file_with_private_key_as_buffer(path, &candidate_public_key).await else { + return Err(ServiceError::CryptoDecryptFailed); + }; + + Ok(buffer) + } + async fn decrypt_private_key( candidate: candidate::Model, password: String, From ad63cc2ff52d92bdd5611cccac874053e98e4f25 Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 13:27:10 +0100 Subject: [PATCH 07/10] fix: mapp_err refactor & error fix --- core/src/services/candidate_service.rs | 54 +++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 98dcb26..7b20b00 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -155,46 +155,46 @@ impl CandidateService { let mut writer = async_zip::write::ZipFileWriter::new(&mut archive); for entry in vec!["MOTIVACNI_DOPIS.pdf", "PORTFOLIO.pdf", "PORTFOLIO.zip"] { - let entry_file = tokio::fs::File::open(cache_path.join(entry)).await; - - let Ok(mut entry_file) = entry_file else { - return Err(ServiceError::FileOpenError); - }; + let mut entry_file = tokio::fs::File::open(cache_path.join(entry)) + .await + .map_err(|_| ServiceError::FileOpenError)?; let mut contents = vec![]; - let read = entry_file.read_to_end(&mut contents).await; - let Ok(_) = read else { - return Err(ServiceError::FileReadError); - }; + entry_file + .read_to_end(&mut contents) + .await + .map_err(|_| ServiceError::FileReadError)?; let builder = async_zip::ZipEntryBuilder::new(entry.to_string(), async_zip::Compression::Deflate); - // TODO: Ne unwrap - let mut entry_writer = writer.write_entry_stream(builder).await.unwrap(); + + let mut entry_writer = writer + .write_entry_stream(builder) + .await + .map_err(|_| ServiceError::FileWriteError)?; // TODO: write_all_buf? - let write = entry_writer.write_all(&mut contents).await; - - let Ok(_) = write else { - return Err(ServiceError::FileWriteError); - }; + entry_writer + .write_all(&mut contents) + .await + .map_err(|_| ServiceError::FileWriteError)?; } // TODO: Ne unwrap writer.close().await.unwrap(); archive.shutdown().await.unwrap(); - let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { - return Err(ServiceError::DbError); - }; + let admin_public_keys = Query::get_all_admin_public_keys(db) + .await + .map_err(|_| ServiceError::DbError)?; - let Ok(candidate) = Query::find_candidate_by_id(db, candidate_id).await else { - return Err(ServiceError::DbError); - }; + let candidate = Query::find_candidate_by_id(db, candidate_id) + .await + .map_err(|_| ServiceError::DbError)?; let Some(candidate) = candidate else { - return Err(ServiceError::UserNotFound); + return Err(ServiceError::CandidateNotFound); }; let candidate_public_key = candidate.public_key; @@ -219,12 +219,12 @@ impl CandidateService { } pub async fn get_portfolio(candidate_id: i32, db: &DbConn) -> Result, ServiceError> { - let Ok(candidate) = Query::find_candidate_by_id(db, candidate_id).await else { - return Err(ServiceError::DbError); - }; + let candidate = Query::find_candidate_by_id(db, candidate_id) + .await + .map_err(|_| ServiceError::DbError)?; let Some(candidate) = candidate else { - return Err(ServiceError::UserNotFound); + return Err(ServiceError::CandidateNotFound); }; let candidate_public_key = candidate.public_key; From b0e2129cf49aec998c1c5398b4c307d4ec81d797 Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 22:02:11 +0100 Subject: [PATCH 08/10] fix: rebasing fixes --- core/src/error.rs | 7 +++++++ core/src/services/candidate_service.rs | 28 +++++++++----------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/core/src/error.rs b/core/src/error.rs index cb7c05a..5aebb06 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -54,6 +54,10 @@ pub enum ServiceError { ArgonHashError(#[from] argon2::password_hash::Error), #[error("AES error")] AesError(#[from] aes_gcm_siv::Error), + #[error("Portfolio is incomplete")] + IncompletePortfolio, + #[error("Zip error")] + ZipError(#[from] async_zip::error::ZipError) } impl ServiceError { @@ -84,6 +88,9 @@ impl ServiceError { ServiceError::TokioJoinError(_) => 500, ServiceError::AesError(_) => 500, ServiceError::ArgonConfigError(_) => 500, + //TODO: Correct code + ServiceError::IncompletePortfolio => 500, + ServiceError::ZipError(_) => 500, } } } diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 7b20b00..2b4ef36 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -146,39 +146,32 @@ impl CandidateService { return Err(ServiceError::IncompletePortfolio); } - let archive = tokio::fs::File::create(path.join("PORTFOLIO.zip")).await; - - let Ok(mut archive) = archive else { - return Err(ServiceError::FileCreationError); - }; + let mut archive = tokio::fs::File::create(path.join("PORTFOLIO.zip")).await?; + let mut writer = async_zip::write::ZipFileWriter::new(&mut archive); for entry in vec!["MOTIVACNI_DOPIS.pdf", "PORTFOLIO.pdf", "PORTFOLIO.zip"] { let mut entry_file = tokio::fs::File::open(cache_path.join(entry)) - .await - .map_err(|_| ServiceError::FileOpenError)?; + .await?; let mut contents = vec![]; entry_file .read_to_end(&mut contents) - .await - .map_err(|_| ServiceError::FileReadError)?; + .await?; let builder = async_zip::ZipEntryBuilder::new(entry.to_string(), async_zip::Compression::Deflate); let mut entry_writer = writer .write_entry_stream(builder) - .await - .map_err(|_| ServiceError::FileWriteError)?; + .await?; // TODO: write_all_buf? entry_writer .write_all(&mut contents) - .await - .map_err(|_| ServiceError::FileWriteError)?; + .await?; } // TODO: Ne unwrap @@ -186,12 +179,10 @@ impl CandidateService { archive.shutdown().await.unwrap(); let admin_public_keys = Query::get_all_admin_public_keys(db) - .await - .map_err(|_| ServiceError::DbError)?; + .await?; let candidate = Query::find_candidate_by_id(db, candidate_id) - .await - .map_err(|_| ServiceError::DbError)?; + .await?; let Some(candidate) = candidate else { return Err(ServiceError::CandidateNotFound); @@ -220,8 +211,7 @@ impl CandidateService { pub async fn get_portfolio(candidate_id: i32, db: &DbConn) -> Result, ServiceError> { let candidate = Query::find_candidate_by_id(db, candidate_id) - .await - .map_err(|_| ServiceError::DbError)?; + .await?; let Some(candidate) = candidate else { return Err(ServiceError::CandidateNotFound); From f1692df7600efd370d258d4359b4338f0d0b94e6 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Mon, 14 Nov 2022 23:04:46 +0100 Subject: [PATCH 09/10] refactor: use '? ' as much as possible --- core/src/services/candidate_service.rs | 38 ++++++-------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 2b4ef36..b2f5458 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -43,19 +43,13 @@ impl CandidateService { return Err(ServiceError::UserAlreadyExists); } - let Ok(hashed_password) = hash_password(plain_text_password.to_string()).await else { - return Err(ServiceError::CryptoHashFailed); - }; + let hashed_password = hash_password(plain_text_password.to_string()).await?; let (pubkey, priv_key_plain_text) = crypto::create_identity(); - let Ok(encrypted_priv_key) = crypto::encrypt_password(priv_key_plain_text, plain_text_password.to_string()).await else { - return Err(ServiceError::CryptoEncryptFailed); - }; + let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text, plain_text_password.to_string()).await?; - let Ok(hashed_personal_id_number) = hash_password(personal_id_number).await else { - return Err(ServiceError::CryptoHashFailed); - }; + let hashed_personal_id_number = hash_password(personal_id_number).await?; // TODO: Specify root path in config? tokio::fs::create_dir_all(Path::new(&application_id.to_string()).join("cache")).await?; @@ -181,12 +175,8 @@ impl CandidateService { let admin_public_keys = Query::get_all_admin_public_keys(db) .await?; - let candidate = Query::find_candidate_by_id(db, candidate_id) - .await?; - - let Some(candidate) = candidate else { - return Err(ServiceError::CandidateNotFound); - }; + let candidate = Query::find_candidate_by_id(db, candidate_id).await? + .ok_or(ServiceError::CandidateNotFound)?; let candidate_public_key = candidate.public_key; @@ -210,20 +200,14 @@ impl CandidateService { } pub async fn get_portfolio(candidate_id: i32, db: &DbConn) -> Result, ServiceError> { - let candidate = Query::find_candidate_by_id(db, candidate_id) - .await?; - - let Some(candidate) = candidate else { - return Err(ServiceError::CandidateNotFound); - }; + let candidate = Query::find_candidate_by_id(db, candidate_id).await? + .ok_or(ServiceError::CandidateNotFound)?; let candidate_public_key = candidate.public_key; let path = Path::new(&candidate_id.to_string()).join("PORTFOLIO.zip"); - let Ok(buffer) = crypto::decrypt_file_with_private_key_as_buffer(path, &candidate_public_key).await else { - return Err(ServiceError::CryptoDecryptFailed); - }; + let buffer = crypto::decrypt_file_with_private_key_as_buffer(path, &candidate_public_key).await?; Ok(buffer) } @@ -234,11 +218,7 @@ impl CandidateService { ) -> Result { let private_key_encrypted = candidate.private_key; - let private_key = crypto::decrypt_password(private_key_encrypted, password).await; - - let Ok(private_key) = private_key else { - return Err(ServiceError::CryptoDecryptFailed); - }; + let private_key = crypto::decrypt_password(private_key_encrypted, password).await?; Ok(private_key) } From 8579cc99874fa6928b40e0c5bbbda2118b754e4c Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Mon, 14 Nov 2022 23:14:55 +0100 Subject: [PATCH 10/10] feat: move 'get_sqlite_conn' to util.rs --- core/src/database/query/candidate.rs | 14 +----------- core/src/database/query/session.rs | 22 +------------------ core/src/lib.rs | 1 + core/src/services/candidate_service.rs | 27 +---------------------- core/src/services/session_service.rs | 27 +---------------------- core/src/util.rs | 30 ++++++++++++++++++++++++++ 6 files changed, 35 insertions(+), 86 deletions(-) create mode 100644 core/src/util.rs diff --git a/core/src/database/query/candidate.rs b/core/src/database/query/candidate.rs index 72ef424..22ca2f1 100644 --- a/core/src/database/query/candidate.rs +++ b/core/src/database/query/candidate.rs @@ -19,19 +19,7 @@ mod tests { use sea_orm::{ActiveModelTrait, DbConn, Set}; use crate::Query; - - #[cfg(test)] - async fn get_memory_sqlite_connection() -> DbConn { - let base_url = "sqlite::memory:"; - let db: DbConn = Database::connect(base_url).await.unwrap(); - - let schema = Schema::new(DbBackend::Sqlite); - let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity); - db.execute(db.get_database_backend().build(&stmt)) - .await - .unwrap(); - db - } + use crate::util::get_memory_sqlite_connection; #[tokio::test] async fn test_find_candidate_by_id() { diff --git a/core/src/database/query/session.rs b/core/src/database/query/session.rs index 2b8ed9c..3b8f361 100644 --- a/core/src/database/query/session.rs +++ b/core/src/database/query/session.rs @@ -24,24 +24,4 @@ impl Query { .all(db) .await } -} - -#[cfg(test)] -mod tests { - use entity::candidate; - use sea_orm::DbConn; - use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, Database, DbBackend, Schema}; - - #[cfg(test)] - async fn get_memory_sqlite_connection() -> DbConn { - let base_url = "sqlite::memory:"; - let db: DbConn = Database::connect(base_url).await.unwrap(); - - let schema = Schema::new(DbBackend::Sqlite); - let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity); - db.execute(db.get_database_backend().build(&stmt)) - .await - .unwrap(); - db - } -} +} \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index c910059..322409c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -4,6 +4,7 @@ pub mod filetype; pub mod services; pub mod error; pub mod candidate_details; +pub mod util; pub use database::mutation::*; pub use database::query::*; diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index b2f5458..592ce8d 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -270,6 +270,7 @@ impl CandidateService { mod tests { use sea_orm::{Database, DbConn}; + use crate::util::get_memory_sqlite_connection; use crate::{crypto, services::candidate_service::CandidateService, Mutation}; use super::EncryptedApplicationDetails; @@ -290,32 +291,6 @@ mod tests { assert!(!CandidateService::is_application_id_valid(101)); } - #[cfg(test)] - async fn get_memory_sqlite_connection() -> DbConn { - use entity::{admin, candidate, parent}; - use sea_orm::Schema; - use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend}; - - let base_url = "sqlite::memory:"; - let db: DbConn = Database::connect(base_url).await.unwrap(); - - 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 stmt3: 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 - } - #[tokio::test] async fn test_encrypt_decrypt_private_key_with_passphrase() { let db = get_memory_sqlite_connection().await; diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index 86827da..33fdb7b 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -171,34 +171,9 @@ mod tests { use crate::{ crypto, - services::{session_service::SessionService, application_service::ApplicationService}, + services::{session_service::SessionService, application_service::ApplicationService}, util::get_memory_sqlite_connection, }; - #[cfg(test)] - async fn get_memory_sqlite_connection() -> DbConn { - let base_url = "sqlite::memory:"; - let db: DbConn = Database::connect(base_url).await.unwrap(); - - 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 stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity); - let stmt4: 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 - } - #[tokio::test] async fn test_create_candidate() { const SECRET: &str = "Tajny_kod"; diff --git a/core/src/util.rs b/core/src/util.rs new file mode 100644 index 0000000..f58b3b8 --- /dev/null +++ b/core/src/util.rs @@ -0,0 +1,30 @@ +use crate::sea_orm::DbConn; + +#[cfg(test)] +pub async fn get_memory_sqlite_connection() -> DbConn { + use entity::{admin, candidate, parent, session}; + use sea_orm::{Schema, Database}; + use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend}; + + let base_url = "sqlite::memory:"; + let db: DbConn = Database::connect(base_url).await.unwrap(); + + 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 stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity); + let stmt4: 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 +} \ No newline at end of file