From 98c59694a78d5c6754a165368d3681d8b37bb355 Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 13:47:14 +0100 Subject: [PATCH 1/4] feat: thiserror PoC --- Cargo.lock | 1 + core/Cargo.toml | 4 ++ core/src/error.rs | 36 ++++++----- core/src/services/admin_service.rs | 8 +-- core/src/services/application_service.rs | 14 ++--- core/src/services/candidate_service.rs | 77 ++++++++++++------------ core/src/services/parent_service.rs | 6 +- core/src/services/session_service.rs | 8 +-- 8 files changed, 79 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4784185..8200336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1906,6 +1906,7 @@ dependencies = [ "sea-orm", "secrecy", "serde", + "thiserror", "tokio", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index 225f103..6ebc7c8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -12,6 +12,10 @@ portfolio-entity = { path = "../entity" } # serde serde = { version = "^1.0", features = ["derive"] } +# error + +thiserror = "^1.0" + # env dotenv = "^0.15" diff --git a/core/src/error.rs b/core/src/error.rs index 0627fdc..2aceac3 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -1,19 +1,39 @@ +use thiserror::Error; + +#[derive(Error, Debug)] + pub enum ServiceError { + #[error("Invalid application id")] InvalidApplicationId, + #[error("Invalid credentials")] InvalidCredentials, + #[error("Forbidden")] Forbidden, + #[error("Session expired, please login agai")] ExpiredSession, + #[error("Error while encoding JWT")] JwtError, + #[error("User already exists")] UserAlreadyExists, + #[error("Candidate not found")] CandidateNotFound, + #[error("Parrent not found")] ParentNotFound, - DbError, + #[error("Database error")] + DbError(#[from] sea_orm::DbErr), + #[error("User not found, please contact technical support")] UserNotFoundByJwtId, + #[error("User not found, please contact technical support")] UserNotFoundBySessionId, + #[error("Crypto hash failed, please contact technical support")] CryptoHashFailed, + #[error("Crypto encryption failed, please contact technical support")] CryptoEncryptFailed, + #[error("Crypto decryption failed, please contact technical support")] CryptoDecryptFailed, + #[error("Candidate details not set, please contact technical support")] CandidateDetailsNotSet, + } impl ServiceError { @@ -27,7 +47,7 @@ impl ServiceError { ServiceError::UserAlreadyExists => (409, "User already exists".to_string()), ServiceError::CandidateNotFound => (404, "User not found".to_string()), ServiceError::ParentNotFound => (500, "Parent not found".to_string()), - ServiceError::DbError => (500, "Database error".to_string()), + ServiceError::DbError(_) => (500, "Database error".to_string()), ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()), ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()), ServiceError::CryptoHashFailed => (500, "Crypto hash failed, please contact technical support".to_string()), @@ -45,15 +65,3 @@ impl ServiceError { self.code_and_message().1 } } - -impl std::fmt::Debug for ServiceError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ServiceError {{ code: {}, message: {} }}", self.code(), self.message()) - } -} - -impl std::fmt::Display for ServiceError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ServiceError {{ code: {}, message: {} }}", self.code(), self.message()) - } -} \ No newline at end of file diff --git a/core/src/services/admin_service.rs b/core/src/services/admin_service.rs index 12dd925..71bda95 100644 --- a/core/src/services/admin_service.rs +++ b/core/src/services/admin_service.rs @@ -13,11 +13,7 @@ impl AdminService { admin_id: i32, password: String, ) -> Result { - let admin = Query::find_admin_by_id(db, admin_id).await; - - let Ok(admin) = admin else { - return Err(ServiceError::DbError); - }; + let admin = Query::find_admin_by_id(db, admin_id).await?; let Some(admin) = admin else { return Err(ServiceError::CandidateNotFound); @@ -55,7 +51,7 @@ impl AdminService { match SessionService::auth_user_session(db, session_uuid).await { Ok(user) => match user { AdminUser::Admin(admin) => Ok(admin), - AdminUser::Candidate(_) => Err(ServiceError::DbError), + AdminUser::Candidate(_) => unreachable!(), }, Err(e) => Err(e), } diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 5793162..048e404 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -34,18 +34,14 @@ impl ApplicationService { form: ApplicationDetails, ) -> Result<(candidate::Model, parent::Model), ServiceError> { let candidate = Query::find_candidate_by_id(db, application) - .await - .map_err(|_| ServiceError::DbError)? + .await? .ok_or(ServiceError::CandidateNotFound)?; let parent = Query::find_parent_by_id(db, application) - .await - .map_err(|_| ServiceError::DbError)? + .await? .ok_or(ServiceError::ParentNotFound)?; - 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?; let mut admin_public_keys_refrence: Vec<&str> = admin_public_keys.iter().map(|s| &**s).collect(); @@ -70,9 +66,9 @@ impl ApplicationService { ) -> Result { let candidate = match Query::find_candidate_by_id(db, application_id).await { Ok(candidate) => candidate.unwrap(), - Err(_) => return Err(ServiceError::DbError), // TODO: logging + Err(e) => return Err(ServiceError::DbError(e)), // TODO: logging }; - let parent = Query::find_parent_by_id(db, application_id).await.unwrap().unwrap(); + let parent = Query::find_parent_by_id(db, application_id).await?.unwrap(); match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { Ok(valid) => { diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index f0e1e9d..dd5bffb 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,13 +1,14 @@ -use entity::{candidate}; +use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; use crate::{ + candidate_details::EncryptedApplicationDetails, crypto::{self, hash_password}, error::ServiceError, - Mutation, Query, candidate_details::{EncryptedApplicationDetails}, + Mutation, Query, }; -use super::{session_service::{AdminUser, SessionService}}; +use super::session_service::{AdminUser, SessionService}; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"]; @@ -53,7 +54,7 @@ impl CandidateService { return Err(ServiceError::CryptoHashFailed); }; - Mutation::create_candidate( + let candidate = Mutation::create_candidate( db, application_id, hashed_password, @@ -61,8 +62,8 @@ impl CandidateService { pubkey, encrypted_priv_key, ) - .await - .map_err(|_| ServiceError::DbError) + .await?; + Ok(candidate) } pub(in crate::services) async fn add_candidate_details( @@ -70,22 +71,21 @@ impl CandidateService { candidate: candidate::Model, enc_details: EncryptedApplicationDetails, ) -> Result { - Mutation::add_candidate_details(db, candidate, enc_details.clone()) - .await - .map_err(|_| ServiceError::DbError) + let model = Mutation::add_candidate_details(db, candidate, enc_details.clone()).await?; + Ok(model) } pub fn is_set_up(candidate: &candidate::Model) -> bool { - candidate.name.is_some() && - candidate.surname.is_some() && - candidate.birthplace.is_some() && - candidate.birthdate.is_some() && - candidate.address.is_some() && - candidate.telephone.is_some() && - candidate.citizenship.is_some() && - candidate.email.is_some() && - candidate.sex.is_some() && - candidate.study.is_some() + candidate.name.is_some() + && candidate.surname.is_some() + && candidate.birthplace.is_some() + && candidate.birthdate.is_some() + && candidate.address.is_some() + && candidate.telephone.is_some() + && candidate.citizenship.is_some() + && candidate.email.is_some() + && candidate.sex.is_some() + && candidate.study.is_some() } pub async fn add_cover_letter(candidate_id: i32, letter: Vec) -> Result<(), ServiceError> { @@ -128,12 +128,12 @@ impl CandidateService { ip_addr: String, ) -> Result<(String, String), ServiceError> { let candidate = Query::find_candidate_by_id(db, candidate_id) - .await - .map_err(|_| ServiceError::DbError)? + .await? .ok_or(ServiceError::CandidateNotFound)?; let session_id = - SessionService::new_session(db, Some(candidate_id), None, password.clone(), ip_addr).await; + SessionService::new_session(db, Some(candidate_id), None, password.clone(), ip_addr) + .await; match session_id { Ok(session_id) => { let private_key = Self::decrypt_private_key(candidate, password).await?; @@ -147,7 +147,7 @@ impl CandidateService { match SessionService::auth_user_session(db, session_uuid).await { Ok(user) => match user { AdminUser::Candidate(candidate) => Ok(candidate), - AdminUser::Admin(_) => Err(ServiceError::DbError), + AdminUser::Admin(_) => unreachable!(), }, Err(e) => Err(e), } @@ -168,17 +168,14 @@ impl CandidateService { mod tests { use sea_orm::{Database, DbConn}; - use crate::{ - crypto, - services::candidate_service::{CandidateService}, Mutation, - }; + use crate::{crypto, services::candidate_service::CandidateService, Mutation}; use super::EncryptedApplicationDetails; use chrono::NaiveDate; - use entity::{parent, candidate}; + use entity::{candidate, parent}; - use crate::services::application_service::ApplicationService; use crate::candidate_details::ApplicationDetails; + use crate::services::application_service::ApplicationService; #[tokio::test] async fn test_application_id_validation() { @@ -225,14 +222,12 @@ mod tests { let secret_message = "trnka".to_string(); - let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) .await .ok() .unwrap(); - Mutation::create_parent(&db, 103151) - .await.unwrap(); + Mutation::create_parent(&db, 103151).await.unwrap(); let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key]) @@ -255,10 +250,15 @@ mod tests { #[cfg(test)] async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) { let plain_text_password = "test".to_string(); - let (candidate, parent) = ApplicationService::create_candidate_with_parent(&db, 103151, &plain_text_password, "".to_string()) - .await - .ok() - .unwrap(); + let (candidate, parent) = ApplicationService::create_candidate_with_parent( + &db, + 103151, + &plain_text_password, + "".to_string(), + ) + .await + .ok() + .unwrap(); let form = ApplicationDetails { name: "test".to_string(), @@ -275,7 +275,6 @@ mod tests { parent_surname: "test".to_string(), parent_telephone: "test".to_string(), parent_email: "test".to_string(), - }; ApplicationService::add_all_details(&db, candidate.application, form) @@ -300,7 +299,9 @@ mod tests { let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password) .await .unwrap(); - let enc_details = EncryptedApplicationDetails::try_from((enc_candidate, enc_parent)).ok().unwrap(); + let enc_details = EncryptedApplicationDetails::try_from((enc_candidate, enc_parent)) + .ok() + .unwrap(); let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap(); assert_eq!(dec_details.name, "test"); // TODO: test every element diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index 79ce638..1990be4 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -11,8 +11,7 @@ impl ParentService { application_id: i32, ) -> Result { let parent = Mutation::create_parent(db, application_id) - .await - .map_err(|_| ServiceError::DbError)?; + .await?; Ok(parent) } @@ -23,8 +22,7 @@ impl ParentService { enc_details: EncryptedApplicationDetails, ) -> Result { let parent = Mutation::add_parent_details(db, parent, enc_details) - .await - .map_err(|_| ServiceError::DbError)?; + .await?; Ok(parent) } diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index a661f10..86827da 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -59,7 +59,7 @@ impl SessionService { Some(candidate) => candidate, None => return Err(ServiceError::CandidateNotFound), }, - Err(_) => return Err(ServiceError::DbError), + Err(e) => return Err(ServiceError::DbError(e)), }; // compare passwords @@ -80,7 +80,7 @@ impl SessionService { Some(admin) => admin, None => return Err(ServiceError::CandidateNotFound), }, - Err(_) => return Err(ServiceError::DbError), + Err(e) => return Err(ServiceError::DbError(e)), }; // compare passwords @@ -102,7 +102,7 @@ impl SessionService { Ok(session) => session, Err(e) => { eprintln!("Error creating session: {}", e); - return Err(ServiceError::DbError); + return Err(ServiceError::DbError(e)); } }; @@ -126,7 +126,7 @@ impl SessionService { Some(session) => session, None => return Err(ServiceError::UserNotFoundBySessionId), }, - Err(_) => return Err(ServiceError::DbError), + Err(e) => return Err(ServiceError::DbError(e)), }; let now = chrono::Utc::now().naive_utc(); From e1ee5b796ac0c18b56cf6060df62fd29c230a7ab Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 14:06:48 +0100 Subject: [PATCH 2/4] feat: convert crypto to our service error --- core/src/crypto.rs | 51 +++++++++++++++++++++++----------------------- core/src/error.rs | 33 +++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 3270091..8fe0582 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -34,9 +34,7 @@ pub fn random_8_char_string() -> String { s } -pub async fn hash_password( - password_plain_text: String, -) -> Result> { +pub async fn hash_password(password_plain_text: String) -> Result { let argon_config = Argon2::new( argon2::Algorithm::Argon2i, argon2::Version::V0x13, @@ -56,13 +54,13 @@ pub async fn hash_password( let hash_string = hash.await??; - return Ok(hash_string); + Ok(hash_string) } pub async fn verify_password( password_plaint_text: String, hash: String, -) -> Result> { +) -> Result { let argon_config = Argon2::new( argon2::Algorithm::Argon2i, argon2::Version::V0x13, @@ -106,7 +104,7 @@ fn convert_key_aes256(key: &str) -> Vec { pub async fn encrypt_password( password_plain_text: String, key: String, -) -> Result> { +) -> Result { let hash = tokio::task::spawn_blocking(move || { let aes_key_nonce = convert_key_aes256(&key); @@ -127,7 +125,7 @@ pub async fn encrypt_password( pub async fn decrypt_password( password_cipher_text: String, key: String, -) -> Result> { +) -> Result { let input = base64::decode(password_cipher_text).unwrap(); let plain = tokio::task::spawn_blocking(move || { let aes_key_nonce = convert_key_aes256(&key); @@ -148,7 +146,7 @@ pub async fn decrypt_password( pub async fn encrypt_password_age( password_plain_text: &str, key: &str, -) -> Result { +) -> Result { let encryptor = age::Encryptor::with_user_passphrase(age::secrecy::Secret::new(key.to_owned())); let mut encrypt_buffer = Vec::new(); @@ -169,7 +167,7 @@ pub async fn encrypt_password_age( pub async fn decrypt_password_age( password_encrypted: &str, key: &str, -) -> Result> { +) -> Result { let encrypted = base64::decode(password_encrypted)?; let decryptor = match age::Decryptor::new_async(&encrypted[..]).await? { @@ -200,7 +198,7 @@ async fn age_encrypt_with_recipients( input_buffer: &[u8], output_buffer: &mut W, recipients: &Vec<&str>, -) -> Result<(), age::EncryptError> { +) -> Result<(), ServiceError> { let public_keys = recipients .into_iter() .map(|recipient| { @@ -233,14 +231,15 @@ async fn age_decrypt_with_private_key( input_buffer: R, output_buffer: &mut Vec, key: &str, -) -> Result<(), Box> { +) -> Result<(), ServiceError> { let decryptor = match age::Decryptor::new_async(input_buffer.compat()).await? { age::Decryptor::Recipients(d) => d, _ => unreachable!(), }; let mut decrypt_writer = decryptor.decrypt_async(iter::once( - &age::x25519::Identity::from_str(key)? as &dyn age::Identity, + &age::x25519::Identity::from_str(key).map_err(|e| ServiceError::AgeKeyError(e.to_string()))? + as &dyn age::Identity, ))?; decrypt_writer.read_to_end(output_buffer).await?; @@ -251,7 +250,7 @@ async fn age_decrypt_with_private_key( pub async fn encrypt_password_with_recipients( password_plain_text: &str, recipients: &Vec<&str>, -) -> Result { +) -> Result { let mut encrypt_buffer = Vec::new(); age_encrypt_with_recipients( @@ -267,25 +266,22 @@ pub async fn encrypt_password_with_recipients( pub async fn decrypt_password_with_private_key( password_encrypted: &str, key: &str, -) -> Result { // TODO More specific error handling - let Ok(encrypted) = base64::decode(password_encrypted) else { - return Err(ServiceError::CryptoEncryptFailed); - }; +) -> Result { + let encrypted = base64::decode(password_encrypted)?; let mut decrypt_buffer = Vec::new(); - if age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await.is_err() { - return Err(ServiceError::CryptoDecryptFailed); - }; + age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await?; - String::from_utf8(decrypt_buffer).map_err(|_| ServiceError::CryptoDecryptFailed) + let string = String::from_utf8(decrypt_buffer)?; + Ok(string) } pub async fn encrypt_file_with_recipients>( plain_file_path: P, cipher_file_path: P, recipients: Vec<&str>, -) -> Result<(), age::EncryptError> { +) -> Result<(), ServiceError> { let mut cipher_file = tokio::fs::File::create(cipher_file_path).await?; let mut plain_file = tokio::fs::File::open(plain_file_path).await?; @@ -293,14 +289,19 @@ pub async fn encrypt_file_with_recipients>( tokio::io::AsyncReadExt::read_to_end(&mut plain_file, &mut plain_file_contents).await?; - age_encrypt_with_recipients(plain_file_contents.as_slice(), &mut cipher_file, &recipients).await + age_encrypt_with_recipients( + plain_file_contents.as_slice(), + &mut cipher_file, + &recipients, + ) + .await } pub async fn decrypt_file_with_private_key>( cipher_file_path: P, plain_file_path: P, key: &str, -) -> Result<(), Box> { +) -> Result<(), ServiceError> { let cipher_file = tokio::fs::File::open(cipher_file_path).await?; let mut plain_file = tokio::fs::File::create(plain_file_path).await?; @@ -316,7 +317,7 @@ pub async fn decrypt_file_with_private_key>( pub async fn decrypt_file_with_private_key_as_buffer>( cipher_file_path: P, key: &str, -) -> Result, Box> { +) -> Result, ServiceError> { let cipher_file = tokio::fs::File::open(cipher_file_path).await?; let mut plain_file = Vec::new(); diff --git a/core/src/error.rs b/core/src/error.rs index 2aceac3..3141ac6 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -33,10 +33,30 @@ pub enum ServiceError { CryptoDecryptFailed, #[error("Candidate details not set, please contact technical support")] CandidateDetailsNotSet, - + #[error("Tokio join error")] + TokioJoinError(#[from] tokio::task::JoinError), + #[error("Age encrypt error")] + AgeEncryptError(#[from] age::EncryptError), + #[error("Age decrypt error")] + AgeDecryptError(#[from] age::DecryptError), + #[error("Age key error")] + AgeKeyError(String), + #[error("IO error")] + IOError(#[from] std::io::Error), + #[error("Base64 decode error")] + Base64DecodeError(#[from] base64::DecodeError), + #[error("UTF8 decode error")] + UTF8DecodeError(#[from] std::string::FromUtf8Error), + #[error("Argon config error")] + ArgonConfigError(#[from] argon2::Error), + #[error("Argon hash error")] + ArgonHashError(#[from] argon2::password_hash::Error), + #[error("AES error")] + AesError(#[from] aes_gcm_siv::Error), } impl ServiceError { + // TODO: Převod do thiserror fn code_and_message(&self) -> (u16, String) { match self { ServiceError::InvalidApplicationId => (400, "Invalid application id".to_string()), @@ -54,6 +74,17 @@ impl ServiceError { 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()), + // TODO: Dodělat hlášky + ServiceError::AgeEncryptError(_) => (500, "Age encrypt error".to_string()), + ServiceError::AgeDecryptError(_) => (500, "Age decrypt error".to_string()), + ServiceError::AgeKeyError(_) => (500, "Age key error".to_string()), + ServiceError::IOError(_) => (500, "IO error".to_string()), + ServiceError::Base64DecodeError(_) => (500, "Base64 decode error".to_string()), + ServiceError::UTF8DecodeError(_) => (500, "UTF8 decode error".to_string()), + ServiceError::ArgonHashError(_) => (500, "Argon hash error".to_string()), + ServiceError::TokioJoinError(_) => (500, "Tokio join error".to_string()), + ServiceError::AesError(_) => (500, "AES error".to_string()), + ServiceError::ArgonConfigError(_) => (500, "Argon config error".to_string()), } } From 54e88de2a9bc85fcdebdf8110ee53f77ab45ddde Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 14:20:31 +0100 Subject: [PATCH 3/4] refactor: remove some unwraps from crypto --- core/src/crypto.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 8fe0582..9eaf538 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -126,7 +126,7 @@ pub async fn decrypt_password( password_cipher_text: String, key: String, ) -> Result { - let input = base64::decode(password_cipher_text).unwrap(); + let input = base64::decode(password_cipher_text)?; let plain = tokio::task::spawn_blocking(move || { let aes_key_nonce = convert_key_aes256(&key); @@ -139,7 +139,7 @@ pub async fn decrypt_password( }) .await??; - Ok(String::from_utf8(plain).unwrap()) + Ok(String::from_utf8(plain)?) } #[deprecated(note = "Too slow, use AES instead")] From 7e3b96e41c0a7c27f321f395840b1cb28aa47fb7 Mon Sep 17 00:00:00 2001 From: EETagent Date: Mon, 14 Nov 2022 20:58:38 +0100 Subject: [PATCH 4/4] refactor: refactor errors, define only codes --- api/src/routes/candidate.rs | 10 +++--- core/src/error.rs | 63 ++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 85486a8..c66d4db 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -75,7 +75,7 @@ pub async fn fill_details( let e = candidate_parent.err().unwrap(); return Err(Custom( Status::from_code(e.code()).unwrap_or_default(), - e.message(), + e.to_string(), )); } @@ -95,7 +95,7 @@ pub async fn get_details( // let handle = tokio::spawn(async move { let details = ApplicationService::decrypt_all_details(db, candidate.application, password) .await - .map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message())); + .map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.to_string())); details.map(|d| Json(d)) } @@ -113,7 +113,7 @@ pub async fn upload_cover_letter( let e = candidate.err().unwrap(); return Err(Custom( Status::from_code(e.code()).unwrap_or_default(), - e.message(), + e.to_string(), )); } @@ -135,7 +135,7 @@ pub async fn upload_portfolio_letter( let e = candidate.err().unwrap(); return Err(Custom( Status::from_code(e.code()).unwrap_or_default(), - e.message(), + e.to_string(), )); } @@ -157,7 +157,7 @@ pub async fn upload_portfolio_zip( let e = candidate.err().unwrap(); return Err(Custom( Status::from_code(e.code()).unwrap_or_default(), - e.message(), + e.to_string(), )); } diff --git a/core/src/error.rs b/core/src/error.rs index 3141ac6..cb7c05a 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -2,6 +2,7 @@ use thiserror::Error; #[derive(Error, Debug)] +// TODO: Lepší hlášky pub enum ServiceError { #[error("Invalid application id")] InvalidApplicationId, @@ -56,43 +57,33 @@ pub enum ServiceError { } impl ServiceError { - // TODO: Převod do thiserror - fn code_and_message(&self) -> (u16, String) { + pub fn code(&self) -> u16 { match self { - ServiceError::InvalidApplicationId => (400, "Invalid application id".to_string()), - ServiceError::InvalidCredentials => (401, "Invalid credentials".to_string()), - ServiceError::Forbidden => (403, "Forbidden".to_string()), - ServiceError::ExpiredSession => (401, "Session expired, please login again".to_string()), - ServiceError::JwtError => (500, "Error while encoding JWT".to_string()), - ServiceError::UserAlreadyExists => (409, "User already exists".to_string()), - ServiceError::CandidateNotFound => (404, "User not found".to_string()), - ServiceError::ParentNotFound => (500, "Parent not found".to_string()), - ServiceError::DbError(_) => (500, "Database error".to_string()), - ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()), - 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()), - // TODO: Dodělat hlášky - ServiceError::AgeEncryptError(_) => (500, "Age encrypt error".to_string()), - ServiceError::AgeDecryptError(_) => (500, "Age decrypt error".to_string()), - ServiceError::AgeKeyError(_) => (500, "Age key error".to_string()), - ServiceError::IOError(_) => (500, "IO error".to_string()), - ServiceError::Base64DecodeError(_) => (500, "Base64 decode error".to_string()), - ServiceError::UTF8DecodeError(_) => (500, "UTF8 decode error".to_string()), - ServiceError::ArgonHashError(_) => (500, "Argon hash error".to_string()), - ServiceError::TokioJoinError(_) => (500, "Tokio join error".to_string()), - ServiceError::AesError(_) => (500, "AES error".to_string()), - ServiceError::ArgonConfigError(_) => (500, "Argon config error".to_string()), + ServiceError::InvalidApplicationId => 400, + ServiceError::InvalidCredentials => 401, + ServiceError::Forbidden => 403, + ServiceError::ExpiredSession => 401, + ServiceError::JwtError => 500, + ServiceError::UserAlreadyExists => 409, + ServiceError::CandidateNotFound => 404, + ServiceError::ParentNotFound => 500, + ServiceError::DbError(_) => 500, + ServiceError::UserNotFoundByJwtId => 500, + ServiceError::UserNotFoundBySessionId => 500, + ServiceError::CryptoHashFailed => 500, + ServiceError::CryptoEncryptFailed => 500, + ServiceError::CryptoDecryptFailed => 500, + ServiceError::CandidateDetailsNotSet => 500, + ServiceError::AgeEncryptError(_) => 500, + ServiceError::AgeDecryptError(_) => 500, + ServiceError::AgeKeyError(_) => 500, + ServiceError::IOError(_) => 500, + ServiceError::Base64DecodeError(_) => 500, + ServiceError::UTF8DecodeError(_) => 500, + ServiceError::ArgonHashError(_) => 500, + ServiceError::TokioJoinError(_) => 500, + ServiceError::AesError(_) => 500, + ServiceError::ArgonConfigError(_) => 500, } } - - pub fn code(&self) -> u16 { - self.code_and_message().0 - } - - pub fn message(&self) -> String { - self.code_and_message().1 - } }