From 7b4707fae6ba87846ef57ad3d985b1aff68fc86d Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 8 Nov 2022 01:22:10 +0100 Subject: [PATCH 1/5] feat: get candidate details --- api/src/lib.rs | 1 + api/src/requests.rs | 6 ++++ api/src/routes/candidate.rs | 47 ++++++++++++++++++++++++++ core/src/crypto.rs | 10 +++--- core/src/services/candidate_service.rs | 13 ++++--- 5 files changed, 66 insertions(+), 11 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index cd1550d..0df83fc 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -42,6 +42,7 @@ async fn start() -> Result<(), rocket::Error> { routes::candidate::login, routes::candidate::whoami, routes::candidate::fill_details, + routes::candidate::get_details, ], ) .mount( diff --git a/api/src/requests.rs b/api/src/requests.rs index 088321e..5288901 100644 --- a/api/src/requests.rs +++ b/api/src/requests.rs @@ -21,4 +21,10 @@ pub struct RegisterRequest { pub struct AdminLoginRequest { pub admin_id: i32, pub password: String, +} + +#[derive(Serialize, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct PasswordRequest { + pub password: String, } \ No newline at end of file diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 1f2ccfb..6f32225 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -8,6 +8,7 @@ use rocket::serde::json::Json; use sea_orm_rocket::Connection; +use crate::requests::PasswordRequest; use crate::{guards::request::auth::CandidateAuth, pool::Db, requests}; #[post("/login", data = "")] @@ -70,3 +71,49 @@ pub async fn fill_details( Ok("Details added".to_string()) } + +#[post("/get_details", data = "")] +pub async fn get_details( + conn: Connection<'_, Db>, + password_form: Json, + session: CandidateAuth, +) -> Result, Custom> { + let db = conn.into_inner(); + let candidate: entity::candidate::Model = session.into(); + let password = password_form.password.clone(); + + // let handle = tokio::spawn(async move { + let details = CandidateService::decrypt_details(db, candidate.application, password).await.map_err(|e| { + Custom( + Status::from_code(e.code()).unwrap_or_default(), + e.message(), + ) + }); + + details.map(|d| Json(d)) +} + +// #[post("/details", data = "")] +// pub async fn get_details( +// conn: Connection<'_, Db>, +// password_form: Json, +// session: CandidateAuth, +// ) -> Result> { +// let db = conn.into_inner(); +// let candidate: entity::candidate::Model = session.into(); +// let password = password_form.password.clone(); + +// let details = CandidateService::decrypt_details(db, candidate.application, password).await; + +// if details.is_err() { +// // TODO cleanup +// let e = details.err().unwrap(); +// return Err(Custom( +// Status::from_code(e.code()).unwrap_or_default(), +// e.message(), +// )); +// } + +// // Ok(Json(details.unwrap())) +// Ok("coming soon".to_string()) +// } diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 8614a87..ec1a973 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -11,6 +11,8 @@ use std::iter; use std::path::Path; use std::str::FromStr; +use crate::error::ServiceError; + /// Foolproof random 8 char string /// only uppercase letters (except for 0 and O) and numbers /// TODO tests @@ -265,14 +267,14 @@ pub async fn encrypt_password_with_recipients( pub async fn decrypt_password_with_private_key( password_encrypted: &str, key: &str, -) -> Result> { - let encrypted = base64::decode(password_encrypted)?; +) -> Result { + let encrypted = base64::decode(password_encrypted).unwrap(); let mut decrypt_buffer = Vec::new(); - age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await?; + age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await.unwrap(); - Ok(String::from_utf8(decrypt_buffer)?) + Ok(String::from_utf8(decrypt_buffer).ok().unwrap()) } pub async fn encrypt_file_with_recipients>( diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 6f6fd5c..53bf992 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,6 +1,6 @@ use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use crate::{ crypto::{self, hash_password}, @@ -169,7 +169,7 @@ impl EncryptedUserDetails { } } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct UserDetails { pub name: String, pub surname: String, @@ -273,11 +273,10 @@ impl CandidateService { 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)?; + let candidate = match Query::find_candidate_by_id(db, candidate_id).await { + Ok(candidate) => candidate.unwrap(), + Err(_) => return Err(ServiceError::DbError), // TODO: logging + }; match crypto::verify_password((&password).to_string(), candidate.code.clone()).await { Ok(valid) => { From e2a9925b9bcef5c0541a4c5f4b3e9e3b7b7e0136 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 8 Nov 2022 01:24:32 +0100 Subject: [PATCH 2/5] style: remove commented code --- api/src/routes/candidate.rs | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 6f32225..1b32b22 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -91,29 +91,4 @@ pub async fn get_details( }); details.map(|d| Json(d)) -} - -// #[post("/details", data = "")] -// pub async fn get_details( -// conn: Connection<'_, Db>, -// password_form: Json, -// session: CandidateAuth, -// ) -> Result> { -// let db = conn.into_inner(); -// let candidate: entity::candidate::Model = session.into(); -// let password = password_form.password.clone(); - -// let details = CandidateService::decrypt_details(db, candidate.application, password).await; - -// if details.is_err() { -// // TODO cleanup -// let e = details.err().unwrap(); -// return Err(Custom( -// Status::from_code(e.code()).unwrap_or_default(), -// e.message(), -// )); -// } - -// // Ok(Json(details.unwrap())) -// Ok("coming soon".to_string()) -// } +} \ No newline at end of file From fc504efd585b1cd7e204e14014bb7558abd7773a Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 8 Nov 2022 01:51:07 +0100 Subject: [PATCH 3/5] fix: remove unused file --- api/src/guards/request/session_auth.rs | 71 -------------------------- 1 file changed, 71 deletions(-) delete mode 100644 api/src/guards/request/session_auth.rs diff --git a/api/src/guards/request/session_auth.rs b/api/src/guards/request/session_auth.rs deleted file mode 100644 index 44063d4..0000000 --- a/api/src/guards/request/session_auth.rs +++ /dev/null @@ -1,71 +0,0 @@ -use entity::candidate::Model as Candidate; -use portfolio_core::sea_orm::prelude::Uuid; -use portfolio_core::services::admin_service::AdminService; -use portfolio_core::services::candidate_service::CandidateService; -use rocket::http::Status; -use rocket::outcome::Outcome; -use rocket::request::{FromRequest, Request}; - -use crate::pool::Db; - -pub struct CandidateAuth(Candidate); - -impl Into for CandidateAuth { - fn into(self) -> Candidate { - self.0 - } -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for CandidateAuth { - type Error = Option; - async fn from_request(req: &'r Request<'_>) -> Outcome { - let session_id = req.cookies().get("id").unwrap().name_value().1; - let conn = &req.rocket().state::().unwrap().conn; - - let uuid = match Uuid::parse_str(&session_id) { - Ok(uuid) => uuid, - Err(_) => return Outcome::Failure((Status::BadRequest, None)), - }; - - let session = CandidateService::auth(conn, uuid).await; - - match session { - Ok(model) => Outcome::Success(CandidateAuth(model)), - Err(_) => Outcome::Failure((Status::Unauthorized, None)), - } - - } -} - -pub struct AdminAuth(Candidate); - -impl Into for AdminAuth { - fn into(self) -> Candidate { - self.0 - } -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for AdminAuth { - type Error = Option; - async fn from_request(req: &'r Request<'_>) -> Outcome { - let session_id = req.cookies().get("id").unwrap().name_value().1; - let conn = &req.rocket().state::().unwrap().conn; - - let uuid = match Uuid::parse_str(&session_id) { - Ok(uuid) => uuid, - Err(_) => return Outcome::Failure((Status::BadRequest, None)), - }; - - let session = AdminService::auth(conn, uuid).await; - - match session { - Ok(model) => Outcome::Success(AdminAuth(model)), - Err(e) => Outcome::Failure( - (Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None) - ), - } - - } -} \ No newline at end of file From a575556bdda11055683f951952ba6bf3764d6f05 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 8 Nov 2022 14:57:25 +0100 Subject: [PATCH 4/5] feat: return ServiceError instead of using unwraps --- core/src/crypto.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/crypto.rs b/core/src/crypto.rs index ec1a973..3270091 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -267,14 +267,18 @@ pub async fn encrypt_password_with_recipients( pub async fn decrypt_password_with_private_key( password_encrypted: &str, key: &str, -) -> Result { - let encrypted = base64::decode(password_encrypted).unwrap(); +) -> Result { // TODO More specific error handling + let Ok(encrypted) = base64::decode(password_encrypted) else { + return Err(ServiceError::CryptoEncryptFailed); + }; let mut decrypt_buffer = Vec::new(); - age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await.unwrap(); + if age_decrypt_with_private_key(encrypted.as_slice(), &mut decrypt_buffer, key).await.is_err() { + return Err(ServiceError::CryptoDecryptFailed); + }; - Ok(String::from_utf8(decrypt_buffer).ok().unwrap()) + String::from_utf8(decrypt_buffer).map_err(|_| ServiceError::CryptoDecryptFailed) } pub async fn encrypt_file_with_recipients>( From 23c1a890c08b39eb57965f110d409555ef580733 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 9 Nov 2022 00:38:37 +0100 Subject: [PATCH 5/5] feat: is_set_up method --- core/src/services/candidate_service.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 53bf992..cc6a041 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -293,6 +293,21 @@ impl CandidateService { enc_details.decrypt(dec_priv_key).await } + pub async fn is_set_up( + candidate: &candidate::Model, + ) -> bool { + candidate.name.is_some() && + candidate.surname.is_some() && + candidate.birthplace.is_some() && + // birthdate: NaiveDate::from_ymd(2000, 1, 1), + 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 login( db: &DbConn, user_id: i32,