From 4b0416695d727da5c1dcdac2b6257213829e7bc3 Mon Sep 17 00:00:00 2001 From: EETagent Date: Thu, 10 Nov 2022 20:05:17 +0100 Subject: [PATCH 1/2] feat: add private key cookie --- api/src/guards/request/auth/admin.rs | 24 ++++++++--- api/src/guards/request/auth/candidate.rs | 22 +++++++--- api/src/routes/admin.rs | 23 ++++++---- api/src/routes/candidate.rs | 37 ++++++++++------- core/src/services/admin_service.rs | 42 +++++++++++++++++-- core/src/services/candidate_service.rs | 53 ++++++++++++++++++++---- 6 files changed, 154 insertions(+), 47 deletions(-) diff --git a/api/src/guards/request/auth/admin.rs b/api/src/guards/request/auth/admin.rs index 0918ed7..62b8bf2 100644 --- a/api/src/guards/request/auth/admin.rs +++ b/api/src/guards/request/auth/admin.rs @@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request}; use crate::pool::Db; -pub struct AdminAuth(Admin); +pub struct AdminAuth(Admin, String); impl Into for AdminAuth { fn into(self) -> Admin { @@ -15,18 +15,30 @@ impl Into for AdminAuth { } } +impl AdminAuth { + pub fn get_private_key(&self) -> String { + self.1.clone() + } +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for AdminAuth { type Error = Option; async fn from_request(req: &'r Request<'_>) -> Outcome { - let cookie = req.cookies().get_private("id"); + let cookie_id = req.cookies().get_private("id"); + let cookie_private_key = req.cookies().get_private("private_key"); - let Some(cookie) = cookie else { + let Some(cookie_id) = cookie_id else { return Outcome::Failure((Status::Unauthorized, None)); }; - let session_id = cookie.name_value().1; - + let Some(cookie_private_key) = cookie_private_key else { + return Outcome::Failure((Status::Unauthorized, None)); + }; + + let session_id = cookie_id.value(); + let private_key = cookie_private_key.value(); + let conn = &req.rocket().state::().unwrap().conn; let uuid = match Uuid::parse_str(&session_id) { @@ -37,7 +49,7 @@ impl<'r> FromRequest<'r> for AdminAuth { let session = AdminService::auth(conn, uuid).await; match session { - Ok(model) => Outcome::Success(AdminAuth(model)), + Ok(model) => Outcome::Success(AdminAuth(model, private_key.to_string())), Err(e) => Outcome::Failure( (Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None) ), diff --git a/api/src/guards/request/auth/candidate.rs b/api/src/guards/request/auth/candidate.rs index f02a1e8..1df82fb 100644 --- a/api/src/guards/request/auth/candidate.rs +++ b/api/src/guards/request/auth/candidate.rs @@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request}; use crate::pool::Db; -pub struct CandidateAuth(Candidate); +pub struct CandidateAuth(Candidate, String); impl Into for CandidateAuth { fn into(self) -> Candidate { @@ -15,19 +15,31 @@ impl Into for CandidateAuth { } } +impl CandidateAuth { + pub fn get_private_key(&self) -> String { + self.1.clone() + } +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for CandidateAuth { type Error = Option; async fn from_request( req: &'r Request<'_>, ) -> Outcome { - let cookie = req.cookies().get_private("id"); + let cookie_id = req.cookies().get_private("id"); + let cookie_private_key = req.cookies().get_private("private_key"); - let Some(cookie) = cookie else { + let Some(cookie_id) = cookie_id else { return Outcome::Failure((Status::Unauthorized, None)); }; - let session_id = cookie.name_value().1; + let Some(cookie_private_key) = cookie_private_key else { + return Outcome::Failure((Status::Unauthorized, None)); + }; + + let session_id = cookie_id.value(); + let private_key = cookie_private_key.value(); let conn = &req.rocket().state::().unwrap().conn; @@ -39,7 +51,7 @@ impl<'r> FromRequest<'r> for CandidateAuth { let session = CandidateService::auth(conn, uuid).await; match session { - Ok(model) => Outcome::Success(CandidateAuth(model)), + Ok(model) => Outcome::Success(CandidateAuth(model, private_key.to_string().to_string())), Err(_) => Outcome::Failure((Status::Unauthorized, None)), } } diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 9f4f1ba..68a6062 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -23,7 +23,7 @@ pub async fn login( let db = conn.into_inner(); println!("{} {}", login_form.admin_id, login_form.password); - let session_token = AdminService::login( + let session_token_key = AdminService::login( db, login_form.admin_id, login_form.password.to_string(), @@ -31,19 +31,28 @@ pub async fn login( ) .await; - if let Err(e) = session_token { + let Ok(session_token_key) = session_token_key else { + let e = session_token_key.unwrap_err(); return Err(Custom( Status::from_code(e.code()).unwrap_or(Status::InternalServerError), e.to_string(), )); - } else { - let session_token = session_token.unwrap(); - cookies.add_private(Cookie::new("id", session_token.clone())); + + }; - return Ok(session_token); - } + let session_token = session_token_key.0; + let private_key = session_token_key.1; + + cookies.add_private(Cookie::new("id", session_token.clone())); + cookies.add_private(Cookie::new("key", private_key.clone())); + + // TODO: JSON + let response = format!("{} {}", session_token, private_key); + + return Ok(response); } + #[get("/whoami")] pub async fn whoami(session: AdminAuth) -> Result> { let admin: entity::admin::Model = session.into(); diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 345ac97..61424be 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -8,9 +8,9 @@ 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::requests::PasswordRequest; use crate::{guards::request::auth::CandidateAuth, pool::Db, requests}; #[post("/login", data = "")] @@ -23,7 +23,7 @@ pub async fn login( let db = conn.into_inner(); println!("{} {}", login_form.application_id, login_form.password); - let session_token = CandidateService::login( + let session_token_key = CandidateService::login( db, login_form.application_id, login_form.password.to_string(), @@ -31,17 +31,23 @@ pub async fn login( ) .await; - if let Err(e) = session_token { + let Ok(session_token_key) = session_token_key else { + let e = session_token_key.unwrap_err(); return Err(Custom( Status::from_code(e.code()).unwrap_or(Status::InternalServerError), e.to_string(), )); - } else { - let session_token = session_token.unwrap(); - cookies.add_private(Cookie::new("id", session_token.clone())); + }; - return Ok(session_token); - } + let session_token = session_token_key.0; + let private_key = session_token_key.1; + + cookies.add_private(Cookie::new("id", session_token.clone())); + cookies.add_private(Cookie::new("key", private_key.clone())); + + let response = format!("{} {}", session_token, private_key); + + return Ok(response); } #[get("/whoami")] @@ -85,12 +91,9 @@ pub async fn get_details( 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(), - ) - }); + 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)) } @@ -122,7 +125,8 @@ pub async fn upload_portfolio_letter( ) -> Result> { let candidate: entity::candidate::Model = session.into(); - let candidate = CandidateService::add_portfolio_letter(candidate.application, letter.into()).await; + let candidate = + CandidateService::add_portfolio_letter(candidate.application, letter.into()).await; if candidate.is_err() { // TODO cleanup @@ -143,7 +147,8 @@ pub async fn upload_portfolio_zip( ) -> Result> { let candidate: entity::candidate::Model = session.into(); - let candidate = CandidateService::add_portfolio_zip(candidate.application, portfolio.into()).await; + let candidate = + CandidateService::add_portfolio_zip(candidate.application, portfolio.into()).await; if candidate.is_err() { // TODO cleanup diff --git a/core/src/services/admin_service.rs b/core/src/services/admin_service.rs index 407b6b4..f6c27a8 100644 --- a/core/src/services/admin_service.rs +++ b/core/src/services/admin_service.rs @@ -1,20 +1,54 @@ use entity::admin; use sea_orm::{prelude::Uuid, DbConn}; -use crate::error::ServiceError; +use crate::{crypto, error::ServiceError, Query}; -use super::session_service::{SessionService, AdminUser}; +use super::session_service::{AdminUser, SessionService}; pub struct AdminService; impl AdminService { + async fn decrypt_private_key( + db: &DbConn, + 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 Some(admin) = admin else { + return Err(ServiceError::UserNotFound); + }; + + let private_key_encrypted = admin.private_key; + + let private_key = crypto::decrypt_password(private_key_encrypted, password).await; + + let Ok(private_key) = private_key else { + return Err(ServiceError::CryptoDecryptFailed); + }; + + Ok(private_key) + } + pub async fn login( db: &DbConn, admin_id: i32, password: String, ip_addr: String, - ) -> Result { - SessionService::new_session(db, None, Some(admin_id), password, ip_addr).await + ) -> Result<(String, String), ServiceError> { + let session_id = + SessionService::new_session(db, None, Some(admin_id), password.clone(), ip_addr).await; + match session_id { + Ok(session_id) => { + let private_key = Self::decrypt_private_key(db, admin_id, password).await?; + Ok((session_id, private_key)) + } + Err(e) => Err(e), + } } pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result { diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 7732197..c63f37d 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -291,10 +291,8 @@ impl CandidateService { enc_details.decrypt(dec_priv_key).await } - pub async fn is_set_up( - candidate: &candidate::Model, - ) -> bool { - candidate.name.is_some() && + 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), @@ -303,7 +301,7 @@ impl CandidateService { candidate.citizenship.is_some() && candidate.email.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) -> Result<(), ServiceError> { @@ -311,23 +309,60 @@ impl CandidateService { Ok(()) } - pub async fn add_portfolio_letter(candidate_id: i32, letter: Vec) -> Result<(), ServiceError> { + pub async fn add_portfolio_letter( + candidate_id: i32, + letter: Vec, + ) -> Result<(), ServiceError> { // TODO Ok(()) } - pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec) -> Result<(), ServiceError> { + pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec) -> Result<(), ServiceError> { // TODO Ok(()) } + async fn decrypt_private_key( + db: &DbConn, + candidate_id: i32, + password: String, + ) -> Result { + let candidate = Query::find_candidate_by_id(db, candidate_id).await; + + let Ok(candidate) = candidate else { + return Err(ServiceError::DbError); + }; + + let Some(candidate) = candidate else { + return Err(ServiceError::UserNotFound); + }; + + 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); + }; + + Ok(private_key) + } + pub async fn login( db: &DbConn, user_id: i32, password: String, ip_addr: String, - ) -> Result { - SessionService::new_session(db, Some(user_id), None, password, ip_addr).await + ) -> Result<(String, String), ServiceError> { + let session_id = + SessionService::new_session(db, Some(user_id), None, password.clone(), ip_addr).await; + match session_id { + Ok(session_id) => { + let private_key = Self::decrypt_private_key(db, user_id, password).await?; + Ok((session_id, private_key)) + } + Err(e) => Err(e), + } } pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result { From c420eb394ab479202bac00a77b79e707844626bd Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 11 Nov 2022 01:07:49 +0100 Subject: [PATCH 2/2] fix: get key cookie value instead of private_key val --- api/src/guards/request/auth/admin.rs | 2 +- api/src/guards/request/auth/candidate.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/guards/request/auth/admin.rs b/api/src/guards/request/auth/admin.rs index 62b8bf2..8f75a31 100644 --- a/api/src/guards/request/auth/admin.rs +++ b/api/src/guards/request/auth/admin.rs @@ -26,7 +26,7 @@ impl<'r> FromRequest<'r> for AdminAuth { type Error = Option; async fn from_request(req: &'r Request<'_>) -> Outcome { let cookie_id = req.cookies().get_private("id"); - let cookie_private_key = req.cookies().get_private("private_key"); + let cookie_private_key = req.cookies().get_private("key"); let Some(cookie_id) = cookie_id else { return Outcome::Failure((Status::Unauthorized, None)); diff --git a/api/src/guards/request/auth/candidate.rs b/api/src/guards/request/auth/candidate.rs index 1df82fb..2f49e1e 100644 --- a/api/src/guards/request/auth/candidate.rs +++ b/api/src/guards/request/auth/candidate.rs @@ -28,7 +28,7 @@ impl<'r> FromRequest<'r> for CandidateAuth { req: &'r Request<'_>, ) -> Outcome { let cookie_id = req.cookies().get_private("id"); - let cookie_private_key = req.cookies().get_private("private_key"); + let cookie_private_key = req.cookies().get_private("key"); let Some(cookie_id) = cookie_id else { return Outcome::Failure((Status::Unauthorized, None));