mirror of
https://github.com/danbulant/Portfolio
synced 2026-05-24 12:35:31 +00:00
Merge pull request #37 from EETagent/private_key_cookie
Add private key cookie for admin & candidate
This commit is contained in:
commit
cfb2fb047b
6 changed files with 154 additions and 47 deletions
|
|
@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request};
|
||||||
|
|
||||||
use crate::pool::Db;
|
use crate::pool::Db;
|
||||||
|
|
||||||
pub struct AdminAuth(Admin);
|
pub struct AdminAuth(Admin, String);
|
||||||
|
|
||||||
impl Into<Admin> for AdminAuth {
|
impl Into<Admin> for AdminAuth {
|
||||||
fn into(self) -> Admin {
|
fn into(self) -> Admin {
|
||||||
|
|
@ -15,17 +15,29 @@ impl Into<Admin> for AdminAuth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AdminAuth {
|
||||||
|
pub fn get_private_key(&self) -> String {
|
||||||
|
self.1.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for AdminAuth {
|
impl<'r> FromRequest<'r> for AdminAuth {
|
||||||
type Error = Option<String>;
|
type Error = Option<String>;
|
||||||
async fn from_request(req: &'r Request<'_>) -> Outcome<AdminAuth, (Status, Self::Error), ()> {
|
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));
|
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 conn = &req.rocket().state::<Db>().unwrap().conn;
|
||||||
|
|
||||||
|
|
@ -37,7 +49,7 @@ impl<'r> FromRequest<'r> for AdminAuth {
|
||||||
let session = AdminService::auth(conn, uuid).await;
|
let session = AdminService::auth(conn, uuid).await;
|
||||||
|
|
||||||
match session {
|
match session {
|
||||||
Ok(model) => Outcome::Success(AdminAuth(model)),
|
Ok(model) => Outcome::Success(AdminAuth(model, private_key.to_string())),
|
||||||
Err(e) => Outcome::Failure(
|
Err(e) => Outcome::Failure(
|
||||||
(Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None)
|
(Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None)
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use rocket::request::{FromRequest, Request};
|
||||||
|
|
||||||
use crate::pool::Db;
|
use crate::pool::Db;
|
||||||
|
|
||||||
pub struct CandidateAuth(Candidate);
|
pub struct CandidateAuth(Candidate, String);
|
||||||
|
|
||||||
impl Into<Candidate> for CandidateAuth {
|
impl Into<Candidate> for CandidateAuth {
|
||||||
fn into(self) -> Candidate {
|
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]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for CandidateAuth {
|
impl<'r> FromRequest<'r> for CandidateAuth {
|
||||||
type Error = Option<String>;
|
type Error = Option<String>;
|
||||||
async fn from_request(
|
async fn from_request(
|
||||||
req: &'r Request<'_>,
|
req: &'r Request<'_>,
|
||||||
) -> Outcome<CandidateAuth, (Status, Self::Error), ()> {
|
) -> 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));
|
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 conn = &req.rocket().state::<Db>().unwrap().conn;
|
||||||
|
|
||||||
|
|
@ -39,7 +51,7 @@ impl<'r> FromRequest<'r> for CandidateAuth {
|
||||||
let session = CandidateService::auth(conn, uuid).await;
|
let session = CandidateService::auth(conn, uuid).await;
|
||||||
|
|
||||||
match session {
|
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)),
|
Err(_) => Outcome::Failure((Status::Unauthorized, None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ pub async fn login(
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
println!("{} {}", login_form.admin_id, login_form.password);
|
println!("{} {}", login_form.admin_id, login_form.password);
|
||||||
|
|
||||||
let session_token = AdminService::login(
|
let session_token_key = AdminService::login(
|
||||||
db,
|
db,
|
||||||
login_form.admin_id,
|
login_form.admin_id,
|
||||||
login_form.password.to_string(),
|
login_form.password.to_string(),
|
||||||
|
|
@ -31,19 +31,28 @@ pub async fn login(
|
||||||
)
|
)
|
||||||
.await;
|
.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(
|
return Err(Custom(
|
||||||
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
|
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
|
||||||
e.to_string(),
|
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")]
|
#[get("/whoami")]
|
||||||
pub async fn whoami(session: AdminAuth) -> Result<String, Custom<String>> {
|
pub async fn whoami(session: AdminAuth) -> Result<String, Custom<String>> {
|
||||||
let admin: entity::admin::Model = session.into();
|
let admin: entity::admin::Model = session.into();
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ use rocket::serde::json::Json;
|
||||||
|
|
||||||
use sea_orm_rocket::Connection;
|
use sea_orm_rocket::Connection;
|
||||||
|
|
||||||
use crate::requests::PasswordRequest;
|
|
||||||
use crate::guards::data::letter::Letter;
|
use crate::guards::data::letter::Letter;
|
||||||
use crate::guards::data::portfolio::Portfolio;
|
use crate::guards::data::portfolio::Portfolio;
|
||||||
|
use crate::requests::PasswordRequest;
|
||||||
use crate::{guards::request::auth::CandidateAuth, pool::Db, requests};
|
use crate::{guards::request::auth::CandidateAuth, pool::Db, requests};
|
||||||
|
|
||||||
#[post("/login", data = "<login_form>")]
|
#[post("/login", data = "<login_form>")]
|
||||||
|
|
@ -23,7 +23,7 @@ pub async fn login(
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
println!("{} {}", login_form.application_id, login_form.password);
|
println!("{} {}", login_form.application_id, login_form.password);
|
||||||
|
|
||||||
let session_token = CandidateService::login(
|
let session_token_key = CandidateService::login(
|
||||||
db,
|
db,
|
||||||
login_form.application_id,
|
login_form.application_id,
|
||||||
login_form.password.to_string(),
|
login_form.password.to_string(),
|
||||||
|
|
@ -31,17 +31,23 @@ pub async fn login(
|
||||||
)
|
)
|
||||||
.await;
|
.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(
|
return Err(Custom(
|
||||||
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
|
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
|
||||||
e.to_string(),
|
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")]
|
#[get("/whoami")]
|
||||||
|
|
@ -85,12 +91,9 @@ pub async fn get_details(
|
||||||
let password = password_form.password.clone();
|
let password = password_form.password.clone();
|
||||||
|
|
||||||
// let handle = tokio::spawn(async move {
|
// let handle = tokio::spawn(async move {
|
||||||
let details = CandidateService::decrypt_details(db, candidate.application, password).await.map_err(|e| {
|
let details = CandidateService::decrypt_details(db, candidate.application, password)
|
||||||
Custom(
|
.await
|
||||||
Status::from_code(e.code()).unwrap_or_default(),
|
.map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()));
|
||||||
e.message(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
details.map(|d| Json(d))
|
details.map(|d| Json(d))
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +125,8 @@ pub async fn upload_portfolio_letter(
|
||||||
) -> Result<String, Custom<String>> {
|
) -> Result<String, Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
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() {
|
if candidate.is_err() {
|
||||||
// TODO cleanup
|
// TODO cleanup
|
||||||
|
|
@ -143,7 +147,8 @@ pub async fn upload_portfolio_zip(
|
||||||
) -> Result<String, Custom<String>> {
|
) -> Result<String, Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
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() {
|
if candidate.is_err() {
|
||||||
// TODO cleanup
|
// TODO cleanup
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,54 @@
|
||||||
use entity::admin;
|
use entity::admin;
|
||||||
use sea_orm::{prelude::Uuid, DbConn};
|
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;
|
pub struct AdminService;
|
||||||
|
|
||||||
impl 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(
|
pub async fn login(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
admin_id: i32,
|
admin_id: i32,
|
||||||
password: String,
|
password: String,
|
||||||
ip_addr: String,
|
ip_addr: String,
|
||||||
) -> Result<String, ServiceError> {
|
) -> Result<(String, String), ServiceError> {
|
||||||
SessionService::new_session(db, None, Some(admin_id), password, ip_addr).await
|
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> {
|
pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<admin::Model, ServiceError> {
|
||||||
|
|
|
||||||
|
|
@ -291,10 +291,8 @@ impl CandidateService {
|
||||||
enc_details.decrypt(dec_priv_key).await
|
enc_details.decrypt(dec_priv_key).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_set_up(
|
pub async fn is_set_up(candidate: &candidate::Model) -> bool {
|
||||||
candidate: &candidate::Model,
|
candidate.name.is_some() &&
|
||||||
) -> bool {
|
|
||||||
candidate.name.is_some() &&
|
|
||||||
candidate.surname.is_some() &&
|
candidate.surname.is_some() &&
|
||||||
candidate.birthplace.is_some() &&
|
candidate.birthplace.is_some() &&
|
||||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
||||||
|
|
@ -311,23 +309,60 @@ impl CandidateService {
|
||||||
Ok(())
|
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
|
// TODO
|
||||||
Ok(())
|
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
|
// TODO
|
||||||
Ok(())
|
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(
|
pub async fn login(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
password: String,
|
password: String,
|
||||||
ip_addr: String,
|
ip_addr: String,
|
||||||
) -> Result<String, ServiceError> {
|
) -> Result<(String, String), ServiceError> {
|
||||||
SessionService::new_session(db, Some(user_id), None, password, ip_addr).await
|
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> {
|
pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<candidate::Model, ServiceError> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue