Merge pull request #37 from EETagent/private_key_cookie

Add private key cookie for admin & candidate
This commit is contained in:
Vojtěch Jungmann 2022-11-11 01:21:30 +01:00 committed by GitHub
commit cfb2fb047b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 47 deletions

View file

@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request};
use crate::pool::Db;
pub struct AdminAuth(Admin);
pub struct AdminAuth(Admin, String);
impl Into<Admin> for AdminAuth {
fn into(self) -> Admin {
@ -15,18 +15,30 @@ impl Into<Admin> 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<String>;
async fn from_request(req: &'r Request<'_>) -> Outcome<AdminAuth, (Status, Self::Error), ()> {
let cookie = req.cookies().get_private("id");
let cookie_id = req.cookies().get_private("id");
let cookie_private_key = req.cookies().get_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::<Db>().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)
),

View file

@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request};
use crate::pool::Db;
pub struct CandidateAuth(Candidate);
pub struct CandidateAuth(Candidate, String);
impl Into<Candidate> for CandidateAuth {
fn into(self) -> Candidate {
@ -15,19 +15,31 @@ impl Into<Candidate> 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<String>;
async fn from_request(
req: &'r Request<'_>,
) -> Outcome<CandidateAuth, (Status, Self::Error), ()> {
let cookie = req.cookies().get_private("id");
let cookie_id = req.cookies().get_private("id");
let cookie_private_key = req.cookies().get_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::<Db>().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)),
}
}

View file

@ -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<String, Custom<String>> {
let admin: entity::admin::Model = session.into();

View file

@ -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 = "<login_form>")]
@ -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<String, Custom<String>> {
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<String, Custom<String>> {
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

View file

@ -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<String, ServiceError> {
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<String, ServiceError> {
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<admin::Model, ServiceError> {

View file

@ -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<u8>) -> Result<(), ServiceError> {
@ -311,23 +309,60 @@ impl CandidateService {
Ok(())
}
pub async fn add_portfolio_letter(candidate_id: i32, letter: Vec<u8>) -> Result<(), ServiceError> {
pub async fn add_portfolio_letter(
candidate_id: i32,
letter: Vec<u8>,
) -> Result<(), ServiceError> {
// TODO
Ok(())
}
pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec<u8>) -> Result<(), ServiceError> {
pub async fn add_portfolio_zip(candidate_id: i32, zip: Vec<u8>) -> Result<(), ServiceError> {
// TODO
Ok(())
}
async fn decrypt_private_key(
db: &DbConn,
candidate_id: i32,
password: String,
) -> Result<String, ServiceError> {
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<String, ServiceError> {
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<candidate::Model, ServiceError> {