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 diff --git a/api/src/lib.rs b/api/src/lib.rs index 61fbd5c..b6517b1 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, routes::candidate::upload_cover_letter, routes::candidate::upload_portfolio_letter, routes::candidate::upload_portfolio_zip, 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 c6e3413..345ac97 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::data::letter::Letter; use crate::guards::data::portfolio::Portfolio; use crate::{guards::request::auth::CandidateAuth, pool::Db, requests}; @@ -73,6 +74,26 @@ 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("/coverletter", data = "")] pub async fn upload_cover_letter( session: CandidateAuth, diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 8614a87..3270091 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,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)?; +) -> 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?; + 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)?) + String::from_utf8(decrypt_buffer).map_err(|_| ServiceError::CryptoDecryptFailed) } pub async fn encrypt_file_with_recipients>( diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 1fe630a..7732197 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, @@ -268,11 +268,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) => { @@ -292,6 +291,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 add_cover_letter(candidate_id: i32, letter: Vec) -> Result<(), ServiceError> { // TODO Ok(())