Merge pull request #43 from EETagent/thiserror

Thiserror
This commit is contained in:
Vojtěch Jungmann 2022-11-14 21:48:55 +01:00 committed by GitHub
commit 6054eb7fd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 130 deletions

1
Cargo.lock generated
View file

@ -1906,6 +1906,7 @@ dependencies = [
"sea-orm", "sea-orm",
"secrecy", "secrecy",
"serde", "serde",
"thiserror",
"tokio", "tokio",
] ]

View file

@ -75,7 +75,7 @@ pub async fn fill_details(
let e = candidate_parent.err().unwrap(); let e = candidate_parent.err().unwrap();
return Err(Custom( return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(), 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 handle = tokio::spawn(async move {
let details = ApplicationService::decrypt_all_details(db, candidate.application, password) let details = ApplicationService::decrypt_all_details(db, candidate.application, password)
.await .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)) details.map(|d| Json(d))
} }
@ -113,7 +113,7 @@ pub async fn upload_cover_letter(
let e = candidate.err().unwrap(); let e = candidate.err().unwrap();
return Err(Custom( return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(), 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(); let e = candidate.err().unwrap();
return Err(Custom( return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(), 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(); let e = candidate.err().unwrap();
return Err(Custom( return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(), Status::from_code(e.code()).unwrap_or_default(),
e.message(), e.to_string(),
)); ));
} }

View file

@ -12,6 +12,10 @@ portfolio-entity = { path = "../entity" }
# serde # serde
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
# error
thiserror = "^1.0"
# env # env
dotenv = "^0.15" dotenv = "^0.15"

View file

@ -34,9 +34,7 @@ pub fn random_8_char_string() -> String {
s s
} }
pub async fn hash_password( pub async fn hash_password(password_plain_text: String) -> Result<String, ServiceError> {
password_plain_text: String,
) -> Result<String, Box<dyn std::error::Error>> {
let argon_config = Argon2::new( let argon_config = Argon2::new(
argon2::Algorithm::Argon2i, argon2::Algorithm::Argon2i,
argon2::Version::V0x13, argon2::Version::V0x13,
@ -56,13 +54,13 @@ pub async fn hash_password(
let hash_string = hash.await??; let hash_string = hash.await??;
return Ok(hash_string); Ok(hash_string)
} }
pub async fn verify_password( pub async fn verify_password(
password_plaint_text: String, password_plaint_text: String,
hash: String, hash: String,
) -> Result<bool, Box<dyn std::error::Error>> { ) -> Result<bool, ServiceError> {
let argon_config = Argon2::new( let argon_config = Argon2::new(
argon2::Algorithm::Argon2i, argon2::Algorithm::Argon2i,
argon2::Version::V0x13, argon2::Version::V0x13,
@ -106,7 +104,7 @@ fn convert_key_aes256(key: &str) -> Vec<u8> {
pub async fn encrypt_password( pub async fn encrypt_password(
password_plain_text: String, password_plain_text: String,
key: String, key: String,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<String, ServiceError> {
let hash = tokio::task::spawn_blocking(move || { let hash = tokio::task::spawn_blocking(move || {
let aes_key_nonce = convert_key_aes256(&key); let aes_key_nonce = convert_key_aes256(&key);
@ -127,8 +125,8 @@ pub async fn encrypt_password(
pub async fn decrypt_password( pub async fn decrypt_password(
password_cipher_text: String, password_cipher_text: String,
key: String, key: String,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<String, ServiceError> {
let input = base64::decode(password_cipher_text).unwrap(); let input = base64::decode(password_cipher_text)?;
let plain = tokio::task::spawn_blocking(move || { let plain = tokio::task::spawn_blocking(move || {
let aes_key_nonce = convert_key_aes256(&key); let aes_key_nonce = convert_key_aes256(&key);
@ -141,14 +139,14 @@ pub async fn decrypt_password(
}) })
.await??; .await??;
Ok(String::from_utf8(plain).unwrap()) Ok(String::from_utf8(plain)?)
} }
#[deprecated(note = "Too slow, use AES instead")] #[deprecated(note = "Too slow, use AES instead")]
pub async fn encrypt_password_age( pub async fn encrypt_password_age(
password_plain_text: &str, password_plain_text: &str,
key: &str, key: &str,
) -> Result<String, age::EncryptError> { ) -> Result<String, ServiceError> {
let encryptor = age::Encryptor::with_user_passphrase(age::secrecy::Secret::new(key.to_owned())); let encryptor = age::Encryptor::with_user_passphrase(age::secrecy::Secret::new(key.to_owned()));
let mut encrypt_buffer = Vec::new(); let mut encrypt_buffer = Vec::new();
@ -169,7 +167,7 @@ pub async fn encrypt_password_age(
pub async fn decrypt_password_age( pub async fn decrypt_password_age(
password_encrypted: &str, password_encrypted: &str,
key: &str, key: &str,
) -> Result<String, Box<dyn std::error::Error>> { ) -> Result<String, ServiceError> {
let encrypted = base64::decode(password_encrypted)?; let encrypted = base64::decode(password_encrypted)?;
let decryptor = match age::Decryptor::new_async(&encrypted[..]).await? { let decryptor = match age::Decryptor::new_async(&encrypted[..]).await? {
@ -200,7 +198,7 @@ async fn age_encrypt_with_recipients<W: tokio::io::AsyncWrite + Unpin>(
input_buffer: &[u8], input_buffer: &[u8],
output_buffer: &mut W, output_buffer: &mut W,
recipients: &Vec<&str>, recipients: &Vec<&str>,
) -> Result<(), age::EncryptError> { ) -> Result<(), ServiceError> {
let public_keys = recipients let public_keys = recipients
.into_iter() .into_iter()
.map(|recipient| { .map(|recipient| {
@ -233,14 +231,15 @@ async fn age_decrypt_with_private_key<R: tokio::io::AsyncRead + Unpin>(
input_buffer: R, input_buffer: R,
output_buffer: &mut Vec<u8>, output_buffer: &mut Vec<u8>,
key: &str, key: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ServiceError> {
let decryptor = match age::Decryptor::new_async(input_buffer.compat()).await? { let decryptor = match age::Decryptor::new_async(input_buffer.compat()).await? {
age::Decryptor::Recipients(d) => d, age::Decryptor::Recipients(d) => d,
_ => unreachable!(), _ => unreachable!(),
}; };
let mut decrypt_writer = decryptor.decrypt_async(iter::once( 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?; decrypt_writer.read_to_end(output_buffer).await?;
@ -251,7 +250,7 @@ async fn age_decrypt_with_private_key<R: tokio::io::AsyncRead + Unpin>(
pub async fn encrypt_password_with_recipients( pub async fn encrypt_password_with_recipients(
password_plain_text: &str, password_plain_text: &str,
recipients: &Vec<&str>, recipients: &Vec<&str>,
) -> Result<String, age::EncryptError> { ) -> Result<String, ServiceError> {
let mut encrypt_buffer = Vec::new(); let mut encrypt_buffer = Vec::new();
age_encrypt_with_recipients( age_encrypt_with_recipients(
@ -267,25 +266,22 @@ pub async fn encrypt_password_with_recipients(
pub async fn decrypt_password_with_private_key( pub async fn decrypt_password_with_private_key(
password_encrypted: &str, password_encrypted: &str,
key: &str, key: &str,
) -> Result<String, ServiceError> { // TODO More specific error handling ) -> Result<String, ServiceError> {
let Ok(encrypted) = base64::decode(password_encrypted) else { let encrypted = base64::decode(password_encrypted)?;
return Err(ServiceError::CryptoEncryptFailed);
};
let mut decrypt_buffer = Vec::new(); let mut decrypt_buffer = Vec::new();
if age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await.is_err() { age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await?;
return Err(ServiceError::CryptoDecryptFailed);
};
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<P: AsRef<Path>>( pub async fn encrypt_file_with_recipients<P: AsRef<Path>>(
plain_file_path: P, plain_file_path: P,
cipher_file_path: P, cipher_file_path: P,
recipients: Vec<&str>, recipients: Vec<&str>,
) -> Result<(), age::EncryptError> { ) -> Result<(), ServiceError> {
let mut cipher_file = tokio::fs::File::create(cipher_file_path).await?; let mut cipher_file = tokio::fs::File::create(cipher_file_path).await?;
let mut plain_file = tokio::fs::File::open(plain_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<P: AsRef<Path>>(
tokio::io::AsyncReadExt::read_to_end(&mut plain_file, &mut plain_file_contents).await?; 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<P: AsRef<Path>>( pub async fn decrypt_file_with_private_key<P: AsRef<Path>>(
cipher_file_path: P, cipher_file_path: P,
plain_file_path: P, plain_file_path: P,
key: &str, key: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), ServiceError> {
let cipher_file = tokio::fs::File::open(cipher_file_path).await?; let cipher_file = tokio::fs::File::open(cipher_file_path).await?;
let mut plain_file = tokio::fs::File::create(plain_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<P: AsRef<Path>>(
pub async fn decrypt_file_with_private_key_as_buffer<P: AsRef<Path>>( pub async fn decrypt_file_with_private_key_as_buffer<P: AsRef<Path>>(
cipher_file_path: P, cipher_file_path: P,
key: &str, key: &str,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> { ) -> Result<Vec<u8>, ServiceError> {
let cipher_file = tokio::fs::File::open(cipher_file_path).await?; let cipher_file = tokio::fs::File::open(cipher_file_path).await?;
let mut plain_file = Vec::new(); let mut plain_file = Vec::new();

View file

@ -1,59 +1,89 @@
use thiserror::Error;
#[derive(Error, Debug)]
// TODO: Lepší hlášky
pub enum ServiceError { pub enum ServiceError {
#[error("Invalid application id")]
InvalidApplicationId, InvalidApplicationId,
#[error("Invalid credentials")]
InvalidCredentials, InvalidCredentials,
#[error("Forbidden")]
Forbidden, Forbidden,
#[error("Session expired, please login agai")]
ExpiredSession, ExpiredSession,
#[error("Error while encoding JWT")]
JwtError, JwtError,
#[error("User already exists")]
UserAlreadyExists, UserAlreadyExists,
#[error("Candidate not found")]
CandidateNotFound, CandidateNotFound,
#[error("Parrent not found")]
ParentNotFound, ParentNotFound,
DbError, #[error("Database error")]
DbError(#[from] sea_orm::DbErr),
#[error("User not found, please contact technical support")]
UserNotFoundByJwtId, UserNotFoundByJwtId,
#[error("User not found, please contact technical support")]
UserNotFoundBySessionId, UserNotFoundBySessionId,
#[error("Crypto hash failed, please contact technical support")]
CryptoHashFailed, CryptoHashFailed,
#[error("Crypto encryption failed, please contact technical support")]
CryptoEncryptFailed, CryptoEncryptFailed,
#[error("Crypto decryption failed, please contact technical support")]
CryptoDecryptFailed, CryptoDecryptFailed,
#[error("Candidate details not set, please contact technical support")]
CandidateDetailsNotSet, 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 { impl ServiceError {
fn code_and_message(&self) -> (u16, String) { pub fn code(&self) -> u16 {
match self { match self {
ServiceError::InvalidApplicationId => (400, "Invalid application id".to_string()), ServiceError::InvalidApplicationId => 400,
ServiceError::InvalidCredentials => (401, "Invalid credentials".to_string()), ServiceError::InvalidCredentials => 401,
ServiceError::Forbidden => (403, "Forbidden".to_string()), ServiceError::Forbidden => 403,
ServiceError::ExpiredSession => (401, "Session expired, please login again".to_string()), ServiceError::ExpiredSession => 401,
ServiceError::JwtError => (500, "Error while encoding JWT".to_string()), ServiceError::JwtError => 500,
ServiceError::UserAlreadyExists => (409, "User already exists".to_string()), ServiceError::UserAlreadyExists => 409,
ServiceError::CandidateNotFound => (404, "User not found".to_string()), ServiceError::CandidateNotFound => 404,
ServiceError::ParentNotFound => (500, "Parent not found".to_string()), ServiceError::ParentNotFound => 500,
ServiceError::DbError => (500, "Database error".to_string()), ServiceError::DbError(_) => 500,
ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()), ServiceError::UserNotFoundByJwtId => 500,
ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()), ServiceError::UserNotFoundBySessionId => 500,
ServiceError::CryptoHashFailed => (500, "Crypto hash failed, please contact technical support".to_string()), ServiceError::CryptoHashFailed => 500,
ServiceError::CryptoEncryptFailed => (500, "Crypto encryption failed, please contact technical support".to_string()), ServiceError::CryptoEncryptFailed => 500,
ServiceError::CryptoDecryptFailed => (500, "Crypto decryption failed, please contact technical support".to_string()), ServiceError::CryptoDecryptFailed => 500,
ServiceError::CandidateDetailsNotSet => (500, "Candidate details not set, please contact technical support".to_string()), 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
}
}
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())
}
} }

View file

@ -13,11 +13,7 @@ impl AdminService {
admin_id: i32, admin_id: i32,
password: String, password: String,
) -> Result<String, ServiceError> { ) -> Result<String, ServiceError> {
let admin = Query::find_admin_by_id(db, admin_id).await; let admin = Query::find_admin_by_id(db, admin_id).await?;
let Ok(admin) = admin else {
return Err(ServiceError::DbError);
};
let Some(admin) = admin else { let Some(admin) = admin else {
return Err(ServiceError::CandidateNotFound); return Err(ServiceError::CandidateNotFound);
@ -55,7 +51,7 @@ impl AdminService {
match SessionService::auth_user_session(db, session_uuid).await { match SessionService::auth_user_session(db, session_uuid).await {
Ok(user) => match user { Ok(user) => match user {
AdminUser::Admin(admin) => Ok(admin), AdminUser::Admin(admin) => Ok(admin),
AdminUser::Candidate(_) => Err(ServiceError::DbError), AdminUser::Candidate(_) => unreachable!(),
}, },
Err(e) => Err(e), Err(e) => Err(e),
} }

View file

@ -34,18 +34,14 @@ impl ApplicationService {
form: ApplicationDetails, form: ApplicationDetails,
) -> Result<(candidate::Model, parent::Model), ServiceError> { ) -> Result<(candidate::Model, parent::Model), ServiceError> {
let candidate = Query::find_candidate_by_id(db, application) let candidate = Query::find_candidate_by_id(db, application)
.await .await?
.map_err(|_| ServiceError::DbError)?
.ok_or(ServiceError::CandidateNotFound)?; .ok_or(ServiceError::CandidateNotFound)?;
let parent = Query::find_parent_by_id(db, application) let parent = Query::find_parent_by_id(db, application)
.await .await?
.map_err(|_| ServiceError::DbError)?
.ok_or(ServiceError::ParentNotFound)?; .ok_or(ServiceError::ParentNotFound)?;
let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else { let admin_public_keys = Query::get_all_admin_public_keys(db).await?;
return Err(ServiceError::DbError);
};
let mut admin_public_keys_refrence: Vec<&str> = let mut admin_public_keys_refrence: Vec<&str> =
admin_public_keys.iter().map(|s| &**s).collect(); admin_public_keys.iter().map(|s| &**s).collect();
@ -70,9 +66,9 @@ impl ApplicationService {
) -> Result<ApplicationDetails, ServiceError> { ) -> Result<ApplicationDetails, ServiceError> {
let candidate = match Query::find_candidate_by_id(db, application_id).await { let candidate = match Query::find_candidate_by_id(db, application_id).await {
Ok(candidate) => candidate.unwrap(), 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 { match crypto::verify_password((&password).to_string(), candidate.code.clone()).await {
Ok(valid) => { Ok(valid) => {

View file

@ -1,13 +1,14 @@
use entity::{candidate}; use entity::candidate;
use sea_orm::{prelude::Uuid, DbConn}; use sea_orm::{prelude::Uuid, DbConn};
use crate::{ use crate::{
candidate_details::EncryptedApplicationDetails,
crypto::{self, hash_password}, crypto::{self, hash_password},
error::ServiceError, 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"]; const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
@ -53,7 +54,7 @@ impl CandidateService {
return Err(ServiceError::CryptoHashFailed); return Err(ServiceError::CryptoHashFailed);
}; };
Mutation::create_candidate( let candidate = Mutation::create_candidate(
db, db,
application_id, application_id,
hashed_password, hashed_password,
@ -61,8 +62,8 @@ impl CandidateService {
pubkey, pubkey,
encrypted_priv_key, encrypted_priv_key,
) )
.await .await?;
.map_err(|_| ServiceError::DbError) Ok(candidate)
} }
pub(in crate::services) async fn add_candidate_details( pub(in crate::services) async fn add_candidate_details(
@ -70,22 +71,21 @@ impl CandidateService {
candidate: candidate::Model, candidate: candidate::Model,
enc_details: EncryptedApplicationDetails, enc_details: EncryptedApplicationDetails,
) -> Result<entity::candidate::Model, ServiceError> { ) -> Result<entity::candidate::Model, ServiceError> {
Mutation::add_candidate_details(db, candidate, enc_details.clone()) let model = Mutation::add_candidate_details(db, candidate, enc_details.clone()).await?;
.await Ok(model)
.map_err(|_| ServiceError::DbError)
} }
pub fn is_set_up(candidate: &candidate::Model) -> bool { pub fn is_set_up(candidate: &candidate::Model) -> bool {
candidate.name.is_some() && candidate.name.is_some()
candidate.surname.is_some() && && candidate.surname.is_some()
candidate.birthplace.is_some() && && candidate.birthplace.is_some()
candidate.birthdate.is_some() && && candidate.birthdate.is_some()
candidate.address.is_some() && && candidate.address.is_some()
candidate.telephone.is_some() && && candidate.telephone.is_some()
candidate.citizenship.is_some() && && candidate.citizenship.is_some()
candidate.email.is_some() && && candidate.email.is_some()
candidate.sex.is_some() && && candidate.sex.is_some()
candidate.study.is_some() && candidate.study.is_some()
} }
pub async fn add_cover_letter(candidate_id: i32, letter: Vec<u8>) -> Result<(), ServiceError> { pub async fn add_cover_letter(candidate_id: i32, letter: Vec<u8>) -> Result<(), ServiceError> {
@ -128,12 +128,12 @@ impl CandidateService {
ip_addr: String, ip_addr: String,
) -> Result<(String, String), ServiceError> { ) -> Result<(String, String), ServiceError> {
let candidate = Query::find_candidate_by_id(db, candidate_id) let candidate = Query::find_candidate_by_id(db, candidate_id)
.await .await?
.map_err(|_| ServiceError::DbError)?
.ok_or(ServiceError::CandidateNotFound)?; .ok_or(ServiceError::CandidateNotFound)?;
let session_id = 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 { match session_id {
Ok(session_id) => { Ok(session_id) => {
let private_key = Self::decrypt_private_key(candidate, password).await?; 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 { match SessionService::auth_user_session(db, session_uuid).await {
Ok(user) => match user { Ok(user) => match user {
AdminUser::Candidate(candidate) => Ok(candidate), AdminUser::Candidate(candidate) => Ok(candidate),
AdminUser::Admin(_) => Err(ServiceError::DbError), AdminUser::Admin(_) => unreachable!(),
}, },
Err(e) => Err(e), Err(e) => Err(e),
} }
@ -168,17 +168,14 @@ impl CandidateService {
mod tests { mod tests {
use sea_orm::{Database, DbConn}; use sea_orm::{Database, DbConn};
use crate::{ use crate::{crypto, services::candidate_service::CandidateService, Mutation};
crypto,
services::candidate_service::{CandidateService}, Mutation,
};
use super::EncryptedApplicationDetails; use super::EncryptedApplicationDetails;
use chrono::NaiveDate; use chrono::NaiveDate;
use entity::{parent, candidate}; use entity::{candidate, parent};
use crate::services::application_service::ApplicationService;
use crate::candidate_details::ApplicationDetails; use crate::candidate_details::ApplicationDetails;
use crate::services::application_service::ApplicationService;
#[tokio::test] #[tokio::test]
async fn test_application_id_validation() { async fn test_application_id_validation() {
@ -225,14 +222,12 @@ mod tests {
let secret_message = "trnka".to_string(); let secret_message = "trnka".to_string();
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()) let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
.await .await
.ok() .ok()
.unwrap(); .unwrap();
Mutation::create_parent(&db, 103151) Mutation::create_parent(&db, 103151).await.unwrap();
.await.unwrap();
let encrypted_message = let encrypted_message =
crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key]) crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key])
@ -255,10 +250,15 @@ mod tests {
#[cfg(test)] #[cfg(test)]
async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) { async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) {
let plain_text_password = "test".to_string(); let plain_text_password = "test".to_string();
let (candidate, parent) = ApplicationService::create_candidate_with_parent(&db, 103151, &plain_text_password, "".to_string()) let (candidate, parent) = ApplicationService::create_candidate_with_parent(
.await &db,
.ok() 103151,
.unwrap(); &plain_text_password,
"".to_string(),
)
.await
.ok()
.unwrap();
let form = ApplicationDetails { let form = ApplicationDetails {
name: "test".to_string(), name: "test".to_string(),
@ -275,7 +275,6 @@ mod tests {
parent_surname: "test".to_string(), parent_surname: "test".to_string(),
parent_telephone: "test".to_string(), parent_telephone: "test".to_string(),
parent_email: "test".to_string(), parent_email: "test".to_string(),
}; };
ApplicationService::add_all_details(&db, candidate.application, form) 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) let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password)
.await .await
.unwrap(); .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(); let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap();
assert_eq!(dec_details.name, "test"); // TODO: test every element assert_eq!(dec_details.name, "test"); // TODO: test every element

View file

@ -11,8 +11,7 @@ impl ParentService {
application_id: i32, application_id: i32,
) -> Result<parent::Model, ServiceError> { ) -> Result<parent::Model, ServiceError> {
let parent = Mutation::create_parent(db, application_id) let parent = Mutation::create_parent(db, application_id)
.await .await?;
.map_err(|_| ServiceError::DbError)?;
Ok(parent) Ok(parent)
} }
@ -23,8 +22,7 @@ impl ParentService {
enc_details: EncryptedApplicationDetails, enc_details: EncryptedApplicationDetails,
) -> Result<parent::Model, ServiceError> { ) -> Result<parent::Model, ServiceError> {
let parent = Mutation::add_parent_details(db, parent, enc_details) let parent = Mutation::add_parent_details(db, parent, enc_details)
.await .await?;
.map_err(|_| ServiceError::DbError)?;
Ok(parent) Ok(parent)
} }

View file

@ -59,7 +59,7 @@ impl SessionService {
Some(candidate) => candidate, Some(candidate) => candidate,
None => return Err(ServiceError::CandidateNotFound), None => return Err(ServiceError::CandidateNotFound),
}, },
Err(_) => return Err(ServiceError::DbError), Err(e) => return Err(ServiceError::DbError(e)),
}; };
// compare passwords // compare passwords
@ -80,7 +80,7 @@ impl SessionService {
Some(admin) => admin, Some(admin) => admin,
None => return Err(ServiceError::CandidateNotFound), None => return Err(ServiceError::CandidateNotFound),
}, },
Err(_) => return Err(ServiceError::DbError), Err(e) => return Err(ServiceError::DbError(e)),
}; };
// compare passwords // compare passwords
@ -102,7 +102,7 @@ impl SessionService {
Ok(session) => session, Ok(session) => session,
Err(e) => { Err(e) => {
eprintln!("Error creating session: {}", e); eprintln!("Error creating session: {}", e);
return Err(ServiceError::DbError); return Err(ServiceError::DbError(e));
} }
}; };
@ -126,7 +126,7 @@ impl SessionService {
Some(session) => session, Some(session) => session,
None => return Err(ServiceError::UserNotFoundBySessionId), None => return Err(ServiceError::UserNotFoundBySessionId),
}, },
Err(_) => return Err(ServiceError::DbError), Err(e) => return Err(ServiceError::DbError(e)),
}; };
let now = chrono::Utc::now().naive_utc(); let now = chrono::Utc::now().naive_utc();