mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-04 18:40:47 +00:00
feat!: separate application and candidate tables
This commit is contained in:
parent
0d8cccb12c
commit
cf25920a5c
31 changed files with 766 additions and 578 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
use entity::candidate::Model as Candidate;
|
use entity::application::Model as Application;
|
||||||
use portfolio_core::models::auth::AuthenticableTrait;
|
use portfolio_core::models::auth::AuthenticableTrait;
|
||||||
use portfolio_core::sea_orm::prelude::Uuid;
|
use portfolio_core::sea_orm::prelude::Uuid;
|
||||||
use portfolio_core::services::candidate_service::CandidateService;
|
use portfolio_core::services::application_service::ApplicationService;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::outcome::Outcome;
|
use rocket::outcome::Outcome;
|
||||||
use rocket::request::{FromRequest, Request};
|
use rocket::request::{FromRequest, Request};
|
||||||
|
|
@ -9,26 +9,26 @@ use rocket::request::{FromRequest, Request};
|
||||||
use crate::logging::format_request;
|
use crate::logging::format_request;
|
||||||
use crate::pool::Db;
|
use crate::pool::Db;
|
||||||
|
|
||||||
pub struct CandidateAuth(Candidate, String);
|
pub struct ApplicationAuth(Application, String);
|
||||||
|
|
||||||
impl Into<Candidate> for CandidateAuth {
|
impl Into<Application> for ApplicationAuth {
|
||||||
fn into(self) -> Candidate {
|
fn into(self) -> Application {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CandidateAuth {
|
impl ApplicationAuth {
|
||||||
pub fn get_private_key(&self) -> String {
|
pub fn get_private_key(&self) -> String {
|
||||||
self.1.clone()
|
self.1.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for CandidateAuth {
|
impl<'r> FromRequest<'r> for ApplicationAuth {
|
||||||
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<ApplicationAuth, (Status, Self::Error), ()> {
|
||||||
let cookie_id = req.cookies().get_private("id");
|
let cookie_id = req.cookies().get_private("id");
|
||||||
let cookie_private_key = req.cookies().get_private("key");
|
let cookie_private_key = req.cookies().get_private("key");
|
||||||
|
|
||||||
|
|
@ -50,12 +50,12 @@ impl<'r> FromRequest<'r> for CandidateAuth {
|
||||||
Err(_) => return Outcome::Failure((Status::BadRequest, None)),
|
Err(_) => return Outcome::Failure((Status::BadRequest, None)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = CandidateService::auth(conn, uuid).await;
|
let session = ApplicationService::auth(conn, uuid).await;
|
||||||
|
|
||||||
match session {
|
match session {
|
||||||
Ok(model) => {
|
Ok(model) => {
|
||||||
info!("{}: CANDIDATE {} AUTHENTICATED", format_request(req), model.application);
|
info!("{}: CANDIDATE {} AUTHENTICATED", format_request(req), model.id);
|
||||||
Outcome::Success(CandidateAuth(model, private_key.to_string().to_string()))
|
Outcome::Success(ApplicationAuth(model, private_key.to_string().to_string()))
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
info!("{}: CANDIDATE {} AUTHENTICATION FAILED", format_request(req), e);
|
info!("{}: CANDIDATE {} AUTHENTICATION FAILED", format_request(req), e);
|
||||||
|
|
|
||||||
|
|
@ -93,12 +93,7 @@ pub async fn create_candidate(
|
||||||
|
|
||||||
let plain_text_password = random_12_char_string();
|
let plain_text_password = random_12_char_string();
|
||||||
|
|
||||||
ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(&db, form.application_id, &plain_text_password, form.personal_id_number.clone())
|
||||||
db,
|
|
||||||
form.application_id,
|
|
||||||
&plain_text_password,
|
|
||||||
form.personal_id_number.clone(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -205,17 +200,19 @@ pub async fn reset_candidate_password(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
session: AdminAuth,
|
session: AdminAuth,
|
||||||
id: i32,
|
id: i32,
|
||||||
) -> Result<Json<CreateCandidateResponse>, Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let db = conn.into_inner();
|
// TODO
|
||||||
|
/* let db = conn.into_inner();
|
||||||
let private_key = session.get_private_key();
|
let private_key = session.get_private_key();
|
||||||
|
|
||||||
let response = CandidateService::reset_password(private_key, db, id)
|
let response = CandidateService::reset_password(private_key, db, id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?; */
|
||||||
|
|
||||||
Ok(
|
Ok(())
|
||||||
|
/* Ok(
|
||||||
Json(response)
|
Json(response)
|
||||||
)
|
) */
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/candidate/<id>/portfolio")]
|
#[get("/candidate/<id>/portfolio")]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
use entity::application;
|
||||||
use portfolio_core::Query;
|
use portfolio_core::Query;
|
||||||
use portfolio_core::models::auth::AuthenticableTrait;
|
use portfolio_core::models::auth::AuthenticableTrait;
|
||||||
use portfolio_core::models::candidate::{ApplicationDetails, NewCandidateResponse};
|
use portfolio_core::models::candidate::{ApplicationDetails, NewCandidateResponse};
|
||||||
|
|
@ -16,7 +17,7 @@ use sea_orm_rocket::Connection;
|
||||||
|
|
||||||
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::{guards::request::auth::CandidateAuth, pool::Db, requests};
|
use crate::{guards::request::auth::ApplicationAuth, pool::Db, requests};
|
||||||
|
|
||||||
use super::to_custom_error;
|
use super::to_custom_error;
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ pub async fn login(
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let ip_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
|
let ip_addr: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
let (session_token, private_key) = CandidateService::login(
|
let (session_token, private_key) = ApplicationService::login(
|
||||||
db,
|
db,
|
||||||
login_form.application_id,
|
login_form.application_id,
|
||||||
login_form.password.to_string(),
|
login_form.password.to_string(),
|
||||||
|
|
@ -47,7 +48,7 @@ pub async fn login(
|
||||||
#[post("/logout")]
|
#[post("/logout")]
|
||||||
pub async fn logout(
|
pub async fn logout(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
_session: CandidateAuth,
|
_session: ApplicationAuth,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
|
|
@ -61,7 +62,7 @@ pub async fn logout(
|
||||||
let session_id = Uuid::try_parse(cookie.value()) // unwrap would be safe here because of the auth guard
|
let session_id = Uuid::try_parse(cookie.value()) // unwrap would be safe here because of the auth guard
|
||||||
.map_err(|e| Custom(Status::BadRequest, e.to_string()))?;
|
.map_err(|e| Custom(Status::BadRequest, e.to_string()))?;
|
||||||
let session = Query::find_session_by_uuid(db, session_id).await.unwrap().unwrap(); // TODO
|
let session = Query::find_session_by_uuid(db, session_id).await.unwrap().unwrap(); // TODO
|
||||||
CandidateService::logout(db, session)
|
ApplicationService::logout(db, session)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -72,9 +73,13 @@ pub async fn logout(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/whoami")]
|
#[get("/whoami")]
|
||||||
pub async fn whoami(session: CandidateAuth) -> Result<Json<NewCandidateResponse>, Custom<String>> {
|
pub async fn whoami(conn: Connection<'_, Db>, session: ApplicationAuth) -> Result<Json<NewCandidateResponse>, Custom<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
let private_key = session.get_private_key();
|
let private_key = session.get_private_key();
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let application: entity::application::Model = session.into();
|
||||||
|
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
|
||||||
|
println!("candidate: {:?}", candidate);
|
||||||
let response = NewCandidateResponse::from_encrypted(&private_key, candidate).await
|
let response = NewCandidateResponse::from_encrypted(&private_key, candidate).await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -86,13 +91,14 @@ pub async fn whoami(session: CandidateAuth) -> Result<Json<NewCandidateResponse>
|
||||||
pub async fn post_details(
|
pub async fn post_details(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
details: Json<ApplicationDetails>,
|
details: Json<ApplicationDetails>,
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
) -> Result<Json<ApplicationDetails>, Custom<String>> {
|
) -> Result<Json<ApplicationDetails>, Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
let form = details.into_inner();
|
let form = details.into_inner();
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let application: application::Model = session.into();
|
||||||
|
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
|
||||||
|
|
||||||
let _candidate_parent = ApplicationService::add_all_details(db, candidate, &form)
|
let _candidate_parent = ApplicationService::add_all_details(db, &application.public_key, candidate, &form)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -102,11 +108,12 @@ pub async fn post_details(
|
||||||
#[get("/details")]
|
#[get("/details")]
|
||||||
pub async fn get_details(
|
pub async fn get_details(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
) -> Result<Json<ApplicationDetails>, Custom<String>> {
|
) -> Result<Json<ApplicationDetails>, Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
let private_key = session.get_private_key();
|
let private_key = session.get_private_key();
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let application: entity::application::Model = session.into();
|
||||||
|
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
|
||||||
|
|
||||||
let details = ApplicationService::decrypt_all_details(private_key, db, candidate)
|
let details = ApplicationService::decrypt_all_details(private_key, db, candidate)
|
||||||
.await
|
.await
|
||||||
|
|
@ -117,12 +124,12 @@ pub async fn get_details(
|
||||||
}
|
}
|
||||||
#[post("/cover_letter", data = "<letter>")]
|
#[post("/cover_letter", data = "<letter>")]
|
||||||
pub async fn upload_cover_letter(
|
pub async fn upload_cover_letter(
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
letter: Letter,
|
letter: Letter,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(candidate.application, letter.into())
|
PortfolioService::add_cover_letter_to_cache(candidate.id, letter.into())
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -130,10 +137,10 @@ pub async fn upload_cover_letter(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/cover_letter")]
|
#[delete("/cover_letter")]
|
||||||
pub async fn delete_cover_letter(session: CandidateAuth) -> Result<(), Custom<String>> {
|
pub async fn delete_cover_letter(session: ApplicationAuth) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::delete_cover_letter_from_cache(candidate.application)
|
PortfolioService::delete_cover_letter_from_cache(candidate.id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -142,12 +149,12 @@ pub async fn delete_cover_letter(session: CandidateAuth) -> Result<(), Custom<St
|
||||||
|
|
||||||
#[post("/portfolio_letter", data = "<letter>")]
|
#[post("/portfolio_letter", data = "<letter>")]
|
||||||
pub async fn upload_portfolio_letter(
|
pub async fn upload_portfolio_letter(
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
letter: Letter,
|
letter: Letter,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::add_portfolio_letter_to_cache(candidate.application, letter.into())
|
PortfolioService::add_portfolio_letter_to_cache(candidate.id, letter.into())
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -155,10 +162,10 @@ pub async fn upload_portfolio_letter(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/portfolio_letter")]
|
#[delete("/portfolio_letter")]
|
||||||
pub async fn delete_portfolio_letter(session: CandidateAuth) -> Result<(), Custom<String>> {
|
pub async fn delete_portfolio_letter(session: ApplicationAuth) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::delete_portfolio_letter_from_cache(candidate.application)
|
PortfolioService::delete_portfolio_letter_from_cache(candidate.id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -167,12 +174,12 @@ pub async fn delete_portfolio_letter(session: CandidateAuth) -> Result<(), Custo
|
||||||
|
|
||||||
#[post("/portfolio_zip", data = "<portfolio>")]
|
#[post("/portfolio_zip", data = "<portfolio>")]
|
||||||
pub async fn upload_portfolio_zip(
|
pub async fn upload_portfolio_zip(
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
portfolio: Portfolio,
|
portfolio: Portfolio,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::add_portfolio_zip_to_cache(candidate.application, portfolio.into())
|
PortfolioService::add_portfolio_zip_to_cache(candidate.id, portfolio.into())
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -180,10 +187,10 @@ pub async fn upload_portfolio_zip(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[delete("/portfolio_zip")]
|
#[delete("/portfolio_zip")]
|
||||||
pub async fn delete_portfolio_zip(session: CandidateAuth) -> Result<(), Custom<String>> {
|
pub async fn delete_portfolio_zip(session: ApplicationAuth) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::delete_portfolio_zip_from_cache(candidate.application)
|
PortfolioService::delete_portfolio_zip_from_cache(candidate.id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -192,11 +199,11 @@ pub async fn delete_portfolio_zip(session: CandidateAuth) -> Result<(), Custom<S
|
||||||
|
|
||||||
#[get("/submission_progress")]
|
#[get("/submission_progress")]
|
||||||
pub async fn submission_progress(
|
pub async fn submission_progress(
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
) -> Result<Json<SubmissionProgress>, Custom<String>> {
|
) -> Result<Json<SubmissionProgress>, Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
let progress = PortfolioService::get_submission_progress(candidate.application)
|
let progress = PortfolioService::get_submission_progress(candidate.id)
|
||||||
.await
|
.await
|
||||||
.map(|x| Json(x))
|
.map(|x| Json(x))
|
||||||
.map_err(to_custom_error);
|
.map_err(to_custom_error);
|
||||||
|
|
@ -207,20 +214,21 @@ pub async fn submission_progress(
|
||||||
#[post("/submit")]
|
#[post("/submit")]
|
||||||
pub async fn submit_portfolio(
|
pub async fn submit_portfolio(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
|
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let application: entity::application::Model = session.into();
|
||||||
|
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
|
||||||
|
|
||||||
let submit = PortfolioService::submit(&candidate, &db).await;
|
let submit = PortfolioService::submit(&application.public_key, &candidate, &db).await;
|
||||||
|
|
||||||
if submit.is_err() {
|
if submit.is_err() {
|
||||||
let e = submit.err().unwrap();
|
let e = submit.err().unwrap();
|
||||||
// Delete on critical error
|
// Delete on critical error
|
||||||
if e.code() == 500 {
|
if e.code() == 500 {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
PortfolioService::delete_portfolio(candidate.application)
|
PortfolioService::delete_portfolio(application.id)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -232,11 +240,11 @@ pub async fn submit_portfolio(
|
||||||
|
|
||||||
#[post("/delete")]
|
#[post("/delete")]
|
||||||
pub async fn delete_portfolio(
|
pub async fn delete_portfolio(
|
||||||
session: CandidateAuth,
|
session: ApplicationAuth,
|
||||||
) -> Result<(), Custom<String>> {
|
) -> Result<(), Custom<String>> {
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
PortfolioService::delete_portfolio(candidate.application)
|
PortfolioService::delete_portfolio(candidate.id)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error)?;
|
.map_err(to_custom_error)?;
|
||||||
|
|
||||||
|
|
@ -244,11 +252,11 @@ pub async fn delete_portfolio(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/download")]
|
#[get("/download")]
|
||||||
pub async fn download_portfolio(session: CandidateAuth) -> Result<Vec<u8>, Custom<String>> {
|
pub async fn download_portfolio(session: ApplicationAuth) -> Result<Vec<u8>, Custom<String>> {
|
||||||
let private_key = session.get_private_key();
|
let private_key = session.get_private_key();
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::application::Model = session.into();
|
||||||
|
|
||||||
let file = PortfolioService::get_portfolio(candidate.application, private_key)
|
let file = PortfolioService::get_portfolio(candidate.id, private_key)
|
||||||
.await
|
.await
|
||||||
.map_err(to_custom_error);
|
.map_err(to_custom_error);
|
||||||
|
|
||||||
|
|
@ -331,7 +339,7 @@ mod tests {
|
||||||
assert_eq!(response.status(), Status::Ok);
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
|
||||||
let candidate = response.into_json::<NewCandidateResponse>().unwrap();
|
let candidate = response.into_json::<NewCandidateResponse>().unwrap();
|
||||||
assert_eq!(candidate.application_id, APPLICATION_ID);
|
// assert_eq!(candidate.id, APPLICATION_ID); // TODO
|
||||||
assert_eq!(candidate.personal_id_number, PERSONAL_ID_NUMBER);
|
assert_eq!(candidate.personal_id_number, PERSONAL_ID_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,21 @@ pub mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(
|
||||||
db,
|
db,
|
||||||
APPLICATION_ID,
|
APPLICATION_ID,
|
||||||
&CANDIDATE_PASSWORD.to_string(),
|
&CANDIDATE_PASSWORD.to_string(),
|
||||||
|
PERSONAL_ID_NUMBER.to_string())
|
||||||
|
.await.unwrap();
|
||||||
|
|
||||||
|
/* ApplicationService::create_candidate_with_parent(
|
||||||
|
db,
|
||||||
|
application,
|
||||||
|
&CANDIDATE_PASSWORD.to_string(),
|
||||||
PERSONAL_ID_NUMBER.to_string(),
|
PERSONAL_ID_NUMBER.to_string(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap(); */
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_client() -> &'static Mutex<Client> {
|
pub fn test_client() -> &'static Mutex<Client> {
|
||||||
|
|
|
||||||
68
core/src/database/mutation/application.rs
Normal file
68
core/src/database/mutation/application.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
use ::entity::application;
|
||||||
|
use log::{info, warn};
|
||||||
|
use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||||
|
|
||||||
|
use crate::Mutation;
|
||||||
|
|
||||||
|
impl Mutation {
|
||||||
|
pub async fn create_application(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
candidate_id: i32,
|
||||||
|
hashed_password: String,
|
||||||
|
enc_personal_id_number: String,
|
||||||
|
pubkey: String,
|
||||||
|
encrypted_priv_key: String,
|
||||||
|
) -> Result<application::Model, DbErr> {
|
||||||
|
let insert = application::ActiveModel {
|
||||||
|
id: Set(application_id),
|
||||||
|
personal_id_number: Set(enc_personal_id_number),
|
||||||
|
password: Set(hashed_password),
|
||||||
|
candidate_id: Set(candidate_id),
|
||||||
|
public_key: Set(pubkey),
|
||||||
|
private_key: Set(encrypted_priv_key),
|
||||||
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
|
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.insert(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("APPLICATION {} CREATED", application_id);
|
||||||
|
Ok(insert)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_candidate_fk(
|
||||||
|
db: &DbConn,
|
||||||
|
application: application::Model,
|
||||||
|
candidate_id: i32,
|
||||||
|
) -> Result<application::Model, DbErr> {
|
||||||
|
let application_id = application.id;
|
||||||
|
let mut application = application.into_active_model();
|
||||||
|
application.candidate_id = Set(candidate_id);
|
||||||
|
|
||||||
|
let update = application.update(db).await?;
|
||||||
|
|
||||||
|
warn!("CANDIDATE {} FK UPDATED", application_id);
|
||||||
|
Ok(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_application_password_and_keys(
|
||||||
|
db: &DbConn,
|
||||||
|
application: application::Model,
|
||||||
|
new_password_hash: String,
|
||||||
|
pub_key: String,
|
||||||
|
priv_key_enc: String,
|
||||||
|
) -> Result<application::Model, DbErr> {
|
||||||
|
let application_id = application.id;
|
||||||
|
let mut application = application.into_active_model();
|
||||||
|
application.password = Set(new_password_hash);
|
||||||
|
application.public_key = Set(pub_key);
|
||||||
|
application.private_key = Set(priv_key_enc);
|
||||||
|
|
||||||
|
let update = application.update(db).await?;
|
||||||
|
|
||||||
|
warn!("CANDIDATE {} PASSWORD CHANGED", application_id);
|
||||||
|
Ok(update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,16 @@
|
||||||
use crate::{Mutation, models::candidate_details::{EncryptedCandidateDetails}};
|
use crate::{Mutation, models::candidate_details::{EncryptedCandidateDetails}};
|
||||||
|
|
||||||
use ::entity::candidate::{self};
|
use ::entity::{candidate::{self}, application};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use sea_orm::*;
|
use sea_orm::*;
|
||||||
|
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
pub async fn create_candidate(
|
pub async fn create_candidate(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
application_id: i32,
|
|
||||||
hashed_password: String,
|
|
||||||
enc_personal_id_number: String,
|
enc_personal_id_number: String,
|
||||||
pubkey: String,
|
|
||||||
encrypted_priv_key: String,
|
|
||||||
) -> Result<candidate::Model, DbErr> {
|
) -> Result<candidate::Model, DbErr> {
|
||||||
let insert = candidate::ActiveModel {
|
let candidate = candidate::ActiveModel {
|
||||||
application: Set(application_id),
|
|
||||||
personal_identification_number: Set(enc_personal_id_number),
|
personal_identification_number: Set(enc_personal_id_number),
|
||||||
code: Set(hashed_password),
|
|
||||||
public_key: Set(pubkey),
|
|
||||||
private_key: Set(encrypted_priv_key),
|
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -26,8 +18,17 @@ impl Mutation {
|
||||||
.insert(db)
|
.insert(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!("CANDIDATE {} CREATED", application_id);
|
info!("CANDIDATE {} CREATED", candidate.application);
|
||||||
Ok(insert)
|
Ok(candidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find_related_application(
|
||||||
|
db: &DbConn,
|
||||||
|
candidate: candidate::Model,
|
||||||
|
) -> Result<Option<application::Model>, DbErr> {
|
||||||
|
candidate.find_related(application::Entity)
|
||||||
|
.one(db)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_candidate(
|
pub async fn delete_candidate(
|
||||||
|
|
@ -41,25 +42,6 @@ impl Mutation {
|
||||||
Ok(delete)
|
Ok(delete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_candidate_password_and_keys(
|
|
||||||
db: &DbConn,
|
|
||||||
candidate: candidate::Model,
|
|
||||||
new_password_hash: String,
|
|
||||||
pub_key: String,
|
|
||||||
priv_key_enc: String,
|
|
||||||
) -> Result<candidate::Model, DbErr> {
|
|
||||||
let application = candidate.application;
|
|
||||||
let mut candidate: candidate::ActiveModel = candidate.into();
|
|
||||||
candidate.code = Set(new_password_hash);
|
|
||||||
candidate.public_key = Set(pub_key);
|
|
||||||
candidate.private_key = Set(priv_key_enc);
|
|
||||||
|
|
||||||
let update = candidate.update(db).await?;
|
|
||||||
|
|
||||||
warn!("CANDIDATE {} PASSWORD CHANGED", application);
|
|
||||||
Ok(update)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_candidate_details(
|
pub async fn update_candidate_details(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
user: candidate::Model,
|
user: candidate::Model,
|
||||||
|
|
@ -103,20 +85,14 @@ mod tests {
|
||||||
async fn test_create_candidate() {
|
async fn test_create_candidate() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103158;
|
let candidate = Mutation::create_candidate(
|
||||||
|
|
||||||
Mutation::create_candidate(
|
|
||||||
&db,
|
&db,
|
||||||
APPLICATION_ID,
|
"".to_string(),
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let candidate = Query::find_candidate_by_id(&db, APPLICATION_ID)
|
let candidate = Query::find_candidate_by_id(&db, candidate.application)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(candidate.is_some());
|
assert!(candidate.is_some());
|
||||||
|
|
@ -126,15 +102,9 @@ mod tests {
|
||||||
async fn test_add_candidate_details() {
|
async fn test_add_candidate_details() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103158;
|
|
||||||
|
|
||||||
let candidate = Mutation::create_candidate(
|
let candidate = Mutation::create_candidate(
|
||||||
&db,
|
&db,
|
||||||
APPLICATION_ID,
|
"".to_string(),
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -144,9 +114,9 @@ mod tests {
|
||||||
vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()],
|
vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()],
|
||||||
).await.unwrap();
|
).await.unwrap();
|
||||||
|
|
||||||
Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap();
|
let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap();
|
||||||
|
|
||||||
let candidate = Query::find_candidate_by_id(&db, APPLICATION_ID)
|
let candidate = Query::find_candidate_by_id(&db, candidate.application)
|
||||||
.await
|
.await
|
||||||
.unwrap().unwrap();
|
.unwrap().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub(crate) struct Mutation;
|
pub(crate) struct Mutation;
|
||||||
|
|
||||||
|
pub mod application;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod candidate;
|
pub mod candidate;
|
||||||
pub mod parent;
|
pub mod parent;
|
||||||
|
|
|
||||||
|
|
@ -49,20 +49,14 @@ mod tests {
|
||||||
async fn test_create_parent() {
|
async fn test_create_parent() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103158;
|
let candidate = Mutation::create_candidate(
|
||||||
|
|
||||||
Mutation::create_candidate(
|
|
||||||
&db,
|
&db,
|
||||||
APPLICATION_ID,
|
"".to_string(),
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_parent = Mutation::create_parent(&db, APPLICATION_ID).await.unwrap();
|
let new_parent = Mutation::create_parent(&db, candidate.application).await.unwrap();
|
||||||
|
|
||||||
let parent = Query::find_parent_by_id(&db, new_parent.id).await.unwrap();
|
let parent = Query::find_parent_by_id(&db, new_parent.id).await.unwrap();
|
||||||
assert!(parent.is_some());
|
assert!(parent.is_some());
|
||||||
|
|
@ -72,20 +66,14 @@ mod tests {
|
||||||
async fn test_add_candidate_details() {
|
async fn test_add_candidate_details() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103158;
|
let candidate = Mutation::create_candidate(
|
||||||
|
|
||||||
Mutation::create_candidate(
|
|
||||||
&db,
|
&db,
|
||||||
APPLICATION_ID,
|
"".to_string(),
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
"test".to_string(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let parent = Mutation::create_parent(&db, APPLICATION_ID).await.unwrap();
|
let parent = Mutation::create_parent(&db, candidate.application).await.unwrap();
|
||||||
|
|
||||||
let encrypted_details: EncryptedApplicationDetails = EncryptedApplicationDetails::new(
|
let encrypted_details: EncryptedApplicationDetails = EncryptedApplicationDetails::new(
|
||||||
&APPLICATION_DETAILS.lock().unwrap().clone(),
|
&APPLICATION_DETAILS.lock().unwrap().clone(),
|
||||||
|
|
|
||||||
25
core/src/database/query/application.rs
Normal file
25
core/src/database/query/application.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use entity::{application, candidate};
|
||||||
|
use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait};
|
||||||
|
|
||||||
|
use crate::Query;
|
||||||
|
|
||||||
|
impl Query {
|
||||||
|
pub async fn find_application_by_id(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
) -> Result<Option<application::Model>, DbErr> {
|
||||||
|
application::Entity::find_by_id(application_id)
|
||||||
|
.one(db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find_related_candidate(
|
||||||
|
db: &DbConn,
|
||||||
|
application: application::Model,
|
||||||
|
) -> Result<Option<candidate::Model>, DbErr> {
|
||||||
|
application
|
||||||
|
.find_related(candidate::Entity)
|
||||||
|
.one(db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,9 +98,6 @@ mod tests {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let candidate = candidate::ActiveModel {
|
let candidate = candidate::ActiveModel {
|
||||||
application: Set(103158),
|
application: Set(103158),
|
||||||
code: Set("test".to_string()),
|
|
||||||
public_key: Set("test".to_string()),
|
|
||||||
private_key: Set("test".to_string()),
|
|
||||||
personal_identification_number: Set("test".to_string()),
|
personal_identification_number: Set("test".to_string()),
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub struct Query;
|
pub struct Query;
|
||||||
|
|
||||||
|
pub mod application;
|
||||||
pub mod candidate;
|
pub mod candidate;
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,6 @@ mod tests {
|
||||||
|
|
||||||
candidate::ActiveModel {
|
candidate::ActiveModel {
|
||||||
application: Set(APPLICATION_ID),
|
application: Set(APPLICATION_ID),
|
||||||
code: Set("test".to_string()),
|
|
||||||
public_key: Set("test".to_string()),
|
|
||||||
private_key: Set("test".to_string()),
|
|
||||||
personal_identification_number: Set("test".to_string()),
|
personal_identification_number: Set("test".to_string()),
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::Query;
|
use crate::Query;
|
||||||
|
|
||||||
use ::entity::prelude::AdminSession;
|
use ::entity::prelude::AdminSession;
|
||||||
use ::entity::{candidate, admin, admin_session};
|
use ::entity::{candidate, admin, admin_session, application};
|
||||||
use ::entity::{session, session::Entity as Session};
|
use ::entity::{session, session::Entity as Session};
|
||||||
use sea_orm::prelude::Uuid;
|
use sea_orm::prelude::Uuid;
|
||||||
use sea_orm::*;
|
use sea_orm::*;
|
||||||
|
|
@ -21,8 +21,8 @@ impl Query {
|
||||||
AdminSession::find_by_id(uuid).one(db).await
|
AdminSession::find_by_id(uuid).one(db).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_related_candidate_sessions(db: &DbConn, candidate: &candidate::Model) -> Result<Vec<session::Model>, DbErr> {
|
pub async fn find_related_application_sessions(db: &DbConn, application: &application::Model) -> Result<Vec<session::Model>, DbErr> {
|
||||||
candidate.find_related(Session)
|
application.find_related(Session)
|
||||||
.order_by_asc(session::Column::UpdatedAt)
|
.order_by_asc(session::Column::UpdatedAt)
|
||||||
.all(db)
|
.all(db)
|
||||||
.await
|
.await
|
||||||
|
|
@ -38,11 +38,12 @@ impl Query {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use entity::{session, admin, candidate, admin_session};
|
use entity::{session, admin, candidate, admin_session, application};
|
||||||
use sea_orm::{prelude::Uuid, ActiveModelTrait, Set};
|
use sea_orm::{prelude::Uuid, ActiveModelTrait, Set};
|
||||||
|
|
||||||
|
use crate::services::candidate_service::tests::put_user_data;
|
||||||
use crate::utils::db::get_memory_sqlite_connection;
|
use crate::utils::db::get_memory_sqlite_connection;
|
||||||
use crate::Query;
|
use crate::{Query, Mutation};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_find_session_by_uuid() {
|
async fn test_find_session_by_uuid() {
|
||||||
|
|
@ -70,23 +71,11 @@ mod tests {
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103158;
|
const APPLICATION_ID: i32 = 103158;
|
||||||
|
|
||||||
let candidate = candidate::ActiveModel {
|
let (application, _, _) = put_user_data(&db).await;
|
||||||
application: Set(APPLICATION_ID),
|
|
||||||
code: Set("test".to_string()),
|
|
||||||
public_key: Set("test".to_string()),
|
|
||||||
private_key: Set("test".to_string()),
|
|
||||||
personal_identification_number: Set("test".to_string()),
|
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
|
||||||
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.insert(&db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
session::ActiveModel {
|
session::ActiveModel {
|
||||||
id: Set(Uuid::new_v4()),
|
id: Set(Uuid::new_v4()),
|
||||||
candidate_id: Set(Some(APPLICATION_ID)),
|
candidate_id: Set(Some(application.id)),
|
||||||
ip_address: Set("10.10.10.10".to_string()),
|
ip_address: Set("10.10.10.10".to_string()),
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
expires_at: Set(chrono::offset::Local::now().naive_local()),
|
expires_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
|
|
@ -126,7 +115,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let sessions = Query::find_related_candidate_sessions(&db, &candidate).await.unwrap();
|
let sessions = Query::find_related_application_sessions(&db, &application).await.unwrap();
|
||||||
assert_eq!(sessions.len(), 1);
|
assert_eq!(sessions.len(), 1);
|
||||||
|
|
||||||
let sessions = Query::find_related_admin_sessions(&db, &admin).await.unwrap();
|
let sessions = Query::find_related_admin_sessions(&db, &admin).await.unwrap();
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@ pub mod tests {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let _admin = insert_test_admin(&db).await;
|
let _admin = insert_test_admin(&db).await;
|
||||||
|
|
||||||
let (candidate, parents) = put_user_data(&db).await;
|
let (application, candidate, parents) = put_user_data(&db).await;
|
||||||
|
|
||||||
let encrypted_details = EncryptedApplicationDetails::try_from((&candidate, parents)).unwrap();
|
let encrypted_details = EncryptedApplicationDetails::try_from((&candidate, parents)).unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,123 @@
|
||||||
use entity::{candidate, parent};
|
use async_trait::async_trait;
|
||||||
use sea_orm::DbConn;
|
use chrono::Duration;
|
||||||
|
use entity::{candidate, parent, application, session};
|
||||||
|
use sea_orm::{DbConn, prelude::Uuid, IntoActiveModel};
|
||||||
|
|
||||||
use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::{EncryptedApplicationDetails}, models::candidate::ApplicationDetails};
|
use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::{EncryptedApplicationDetails}, models::{candidate::ApplicationDetails, candidate_details::EncryptedString, auth::AuthenticableTrait}, Mutation, crypto::{hash_password, self}};
|
||||||
|
|
||||||
use super::{parent_service::ParentService, candidate_service::CandidateService};
|
use super::{parent_service::ParentService, candidate_service::CandidateService, session_service::SessionService};
|
||||||
|
|
||||||
|
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
|
||||||
|
|
||||||
pub struct ApplicationService;
|
pub struct ApplicationService;
|
||||||
|
|
||||||
impl ApplicationService {
|
impl ApplicationService {
|
||||||
pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩🍼
|
/// Creates a new candidate with:
|
||||||
|
/// Encrypted personal identification number
|
||||||
|
/// Hashed password
|
||||||
|
/// Encrypted private key
|
||||||
|
/// Public key
|
||||||
|
pub async fn create(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
application_id: i32,
|
application_id: i32,
|
||||||
plain_text_password: &String,
|
plain_text_password: &String,
|
||||||
personal_id_number: String,
|
personal_id_number: String,
|
||||||
) -> Result<(candidate::Model, parent::Model), ServiceError> {
|
) -> Result<application::Model, ServiceError> {
|
||||||
|
// Check if application id starts with 101, 102 or 103
|
||||||
|
if !Self::is_application_id_valid(application_id) {
|
||||||
|
return Err(ServiceError::InvalidApplicationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user with that application id already exists
|
||||||
|
if Query::find_application_by_id(db, application_id)
|
||||||
|
.await?
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return Err(ServiceError::UserAlreadyExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let hashed_password = hash_password(plain_text_password.to_string()).await?;
|
||||||
|
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
||||||
|
let encrypted_priv_key = crypto::encrypt_password(
|
||||||
|
priv_key_plain_text,
|
||||||
|
plain_text_password.to_string()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
let recipients = get_recipients(db, &pubkey).await?;
|
||||||
|
let enc_personal_id_number = EncryptedString::new(
|
||||||
|
&personal_id_number,
|
||||||
|
&recipients,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
let candidate = CandidateService::create(
|
||||||
|
db,
|
||||||
|
enc_personal_id_number.clone().to_string()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
println!("candidate: {:?}", candidate);
|
||||||
|
|
||||||
|
let application = Mutation::create_application(
|
||||||
|
db,
|
||||||
|
application_id,
|
||||||
|
candidate.application,
|
||||||
|
hashed_password,
|
||||||
|
enc_personal_id_number.to_string(),
|
||||||
|
pubkey,
|
||||||
|
encrypted_priv_key,
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
Ok(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_application_id_valid(application_id: i32) -> bool {
|
||||||
|
let s = &application_id.to_string();
|
||||||
|
if s.len() <= 3 {
|
||||||
|
// TODO: does the field of study prefix have to be exactly 6 digits? VYRESIT PODLE PRIHLASEK!!!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let field_of_study_prefix = &s[0..3];
|
||||||
|
FIELD_OF_STUDY_PREFIXES.contains(&field_of_study_prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩🍼
|
||||||
|
db: &DbConn,
|
||||||
|
application: application::Model,
|
||||||
|
plain_text_password: &String,
|
||||||
|
personal_id_number: String,
|
||||||
|
) -> Result<(application::Model, candidate::Model, parent::Model), ServiceError> {
|
||||||
|
let candidate = CandidateService::create(db, personal_id_number).await?;
|
||||||
|
let parent = ParentService::create(db, candidate.application).await?;
|
||||||
|
let application = Mutation::update_candidate_fk(db, application, candidate.application).await?;
|
||||||
Ok(
|
Ok(
|
||||||
(
|
(
|
||||||
CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?,
|
application,
|
||||||
ParentService::create(db, application_id).await?
|
candidate,
|
||||||
|
parent
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_related_candidate(
|
||||||
|
db: &DbConn,
|
||||||
|
application: application::Model,
|
||||||
|
) -> Result<candidate::Model, ServiceError> {
|
||||||
|
let candidate = Query::find_related_candidate(db, application).await?;
|
||||||
|
if let Some(candidate) = candidate {
|
||||||
|
Ok(candidate)
|
||||||
|
} else {
|
||||||
|
Err(ServiceError::CandidateNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn add_all_details(
|
pub async fn add_all_details(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
|
public_key: &String,
|
||||||
candidate: candidate::Model,
|
candidate: candidate::Model,
|
||||||
form: &ApplicationDetails,
|
form: &ApplicationDetails,
|
||||||
) -> Result<(candidate::Model, Vec<parent::Model>), ServiceError> {
|
) -> Result<(candidate::Model, Vec<parent::Model>), ServiceError> {
|
||||||
|
|
||||||
let recipients = get_recipients(db, &candidate.public_key).await?;
|
let recipients = get_recipients(db, public_key).await?;
|
||||||
let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients).await?;
|
let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients).await?;
|
||||||
let parents = ParentService::add_parents_details(db, &candidate, &form.parents, &recipients).await?;
|
let parents = ParentService::add_parents_details(db, &candidate, &form.parents, &recipients).await?;
|
||||||
Ok(
|
Ok(
|
||||||
|
|
@ -55,4 +144,228 @@ impl ApplicationService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn decrypt_private_key(
|
||||||
|
application: application::Model,
|
||||||
|
password: String,
|
||||||
|
) -> Result<String, ServiceError> {
|
||||||
|
let private_key_encrypted = application.private_key;
|
||||||
|
|
||||||
|
let private_key = crypto::decrypt_password(private_key_encrypted, password).await?;
|
||||||
|
|
||||||
|
Ok(private_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn extend_session_duration_to_14_days(db: &DbConn, session: session::Model) -> Result<session::Model, ServiceError> {
|
||||||
|
let now = chrono::Utc::now().naive_utc();
|
||||||
|
if now >= session.updated_at.checked_add_signed(Duration::days(1)).ok_or(ServiceError::Unauthorized)? {
|
||||||
|
let new_expires_at = now.checked_add_signed(Duration::days(14)).ok_or(ServiceError::Unauthorized)?;
|
||||||
|
|
||||||
|
Ok(Mutation::update_session_expiration(db, session, new_expires_at).await?)
|
||||||
|
} else {
|
||||||
|
Ok(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/* pub async fn reset_password(
|
||||||
|
admin_private_key: String,
|
||||||
|
db: &DbConn,
|
||||||
|
id: i32,
|
||||||
|
) -> Result<CreateCandidateResponse, ServiceError> {
|
||||||
|
let candidate = Query::find_candidate_by_id(db, id).await?
|
||||||
|
.ok_or(ServiceError::CandidateNotFound)?;
|
||||||
|
let parents = Query::find_candidate_parents(db, &candidate).await?;
|
||||||
|
|
||||||
|
|
||||||
|
let new_password_plain = crypto::random_12_char_string();
|
||||||
|
let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?;
|
||||||
|
|
||||||
|
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
||||||
|
let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text.clone(),
|
||||||
|
new_password_plain.to_string()
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
|
||||||
|
Self::delete_old_sessions(db, &candidate, 0).await?;
|
||||||
|
let candidate = Mutation::update_candidate_password_and_keys(db,
|
||||||
|
candidate,
|
||||||
|
new_password_hash,
|
||||||
|
pubkey.clone(),
|
||||||
|
encrypted_priv_key
|
||||||
|
).await?;
|
||||||
|
|
||||||
|
|
||||||
|
// user might no have filled his details yet, but personal id number is filled from beginning
|
||||||
|
let personal_id_number = EncryptedString::from(candidate.personal_identification_number.clone())
|
||||||
|
.decrypt(&admin_private_key)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let recipients = get_recipients(db, &pubkey).await?;
|
||||||
|
|
||||||
|
let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone()))
|
||||||
|
.decrypt(admin_private_key).await?;
|
||||||
|
let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?;
|
||||||
|
|
||||||
|
Mutation::update_candidate_details(db, candidate, enc_details.candidate).await?;
|
||||||
|
for i in 0..enc_details.parents.len() {
|
||||||
|
Mutation::add_parent_details(db, parents[i].clone(), enc_details.parents[i].clone()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
CreateCandidateResponse {
|
||||||
|
application_id: id,
|
||||||
|
personal_id_number,
|
||||||
|
password: new_password_plain,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AuthenticableTrait for ApplicationService {
|
||||||
|
type User = application::Model;
|
||||||
|
type Session = session::Model;
|
||||||
|
|
||||||
|
async fn login(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
password: String,
|
||||||
|
ip_addr: String,
|
||||||
|
) -> Result<(String, String), ServiceError> {
|
||||||
|
let application = Query::find_application_by_id(db, application_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(ServiceError::CandidateNotFound)?;
|
||||||
|
|
||||||
|
let session_id = Self::new_session(db, &application, password.clone(), ip_addr).await?;
|
||||||
|
|
||||||
|
let private_key = Self::decrypt_private_key(application, password).await?;
|
||||||
|
Ok((session_id, private_key))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<application::Model, ServiceError> {
|
||||||
|
let session = Query::find_session_by_uuid(db, session_uuid)
|
||||||
|
.await?
|
||||||
|
.ok_or(ServiceError::Unauthorized)?;
|
||||||
|
|
||||||
|
if !SessionService::is_valid(&session).await? {
|
||||||
|
Mutation::delete_session(db, session.into_active_model()).await?;
|
||||||
|
return Err(ServiceError::ExpiredSession);
|
||||||
|
}
|
||||||
|
// Candidate authenticated
|
||||||
|
|
||||||
|
Self::extend_session_duration_to_14_days(db, session.clone()).await?;
|
||||||
|
|
||||||
|
let application = Query::find_application_by_id(db, session.candidate_id.unwrap())
|
||||||
|
.await?
|
||||||
|
.ok_or(ServiceError::CandidateNotFound)?;
|
||||||
|
|
||||||
|
Ok(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn logout(db: &DbConn, session: session::Model) -> Result<(), ServiceError> {
|
||||||
|
Mutation::delete_session(db, session.into_active_model()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_session(
|
||||||
|
db: &DbConn,
|
||||||
|
application: &application::Model,
|
||||||
|
password: String,
|
||||||
|
ip_addr: String,
|
||||||
|
) -> Result<String, ServiceError> {
|
||||||
|
if !crypto::verify_password(password.clone(), application.password.clone()).await? {
|
||||||
|
return Err(ServiceError::InvalidCredentials);
|
||||||
|
}
|
||||||
|
// user is authenticated, generate a new session
|
||||||
|
let random_uuid: Uuid = Uuid::new_v4();
|
||||||
|
|
||||||
|
let session = Mutation::insert_candidate_session(db, random_uuid, application.id, ip_addr).await?;
|
||||||
|
|
||||||
|
Self::delete_old_sessions(db, &application, 3).await?;
|
||||||
|
|
||||||
|
Ok(session.id.to_string())
|
||||||
|
}
|
||||||
|
async fn delete_old_sessions(
|
||||||
|
db: &DbConn,
|
||||||
|
application: &application::Model,
|
||||||
|
keep_n_recent: usize,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
|
let sessions = Query::find_related_application_sessions(db, &application)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_owned().into_active_model())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
SessionService::delete_sessions(db, sessions, keep_n_recent).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tests {
|
||||||
|
use crate::{utils::db::get_memory_sqlite_connection, services::{application_service::ApplicationService}, crypto};
|
||||||
|
|
||||||
|
const APPLICATION_ID: i32 = 103151;
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_application_id_validation() {
|
||||||
|
assert!(ApplicationService::is_application_id_valid(101_101));
|
||||||
|
assert!(ApplicationService::is_application_id_valid(102_107));
|
||||||
|
assert!(ApplicationService::is_application_id_valid(103_109));
|
||||||
|
assert!(!ApplicationService::is_application_id_valid(104_109));
|
||||||
|
assert!(!ApplicationService::is_application_id_valid(100_109));
|
||||||
|
assert!(!ApplicationService::is_application_id_valid(201_109));
|
||||||
|
assert!(!ApplicationService::is_application_id_valid(101));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/* #[tokio::test]
|
||||||
|
async fn test_password_reset() {
|
||||||
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
let admin = create_admin(&db).await;
|
||||||
|
let (candidate, _parent) = put_user_data(&db).await;
|
||||||
|
|
||||||
|
let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_password = CandidateService::reset_password(private_key, &db, candidate.application).await.unwrap().password;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
CandidateService::login(&db, candidate.application, new_password, "127.0.0.1".to_string()).await.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_encrypt_decrypt_private_key_with_passphrase() {
|
||||||
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
|
let plain_text_password = "test".to_string();
|
||||||
|
|
||||||
|
let secret_message = "trnka".to_string();
|
||||||
|
|
||||||
|
let application = ApplicationService::create(&db, 103100, &plain_text_password, "".to_string()).await.unwrap();
|
||||||
|
|
||||||
|
let encrypted_message =
|
||||||
|
crypto::encrypt_password_with_recipients(&secret_message, &vec![&application.public_key])
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let private_key_plain_text =
|
||||||
|
crypto::decrypt_password(application.private_key, plain_text_password)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let decrypted_message =
|
||||||
|
crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(secret_message, decrypted_message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use entity::{candidate, session};
|
use entity::{candidate, session, application};
|
||||||
use sea_orm::{prelude::Uuid, DbConn, IntoActiveModel};
|
use sea_orm::{prelude::Uuid, DbConn, IntoActiveModel};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -12,9 +12,6 @@ use crate::{
|
||||||
|
|
||||||
use super::{session_service::SessionService, portfolio_service::PortfolioService};
|
use super::{session_service::SessionService, portfolio_service::PortfolioService};
|
||||||
|
|
||||||
|
|
||||||
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
|
|
||||||
|
|
||||||
pub struct CandidateService;
|
pub struct CandidateService;
|
||||||
|
|
||||||
impl CandidateService {
|
impl CandidateService {
|
||||||
|
|
@ -25,104 +22,20 @@ impl CandidateService {
|
||||||
/// Public key
|
/// Public key
|
||||||
pub(in crate::services) async fn create(
|
pub(in crate::services) async fn create(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
application_id: i32,
|
enc_personal_id_number: String,
|
||||||
plain_text_password: &String,
|
|
||||||
personal_id_number: String,
|
|
||||||
) -> Result<candidate::Model, ServiceError> {
|
) -> Result<candidate::Model, ServiceError> {
|
||||||
// Check if application id starts with 101, 102 or 103
|
|
||||||
if !CandidateService::is_application_id_valid(application_id) {
|
|
||||||
return Err(ServiceError::InvalidApplicationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user with that application id already exists
|
|
||||||
if Query::find_candidate_by_id(db, application_id)
|
|
||||||
.await?
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return Err(ServiceError::UserAlreadyExists);
|
|
||||||
}
|
|
||||||
PortfolioService::create_user_dir(application_id).await?;
|
|
||||||
|
|
||||||
|
|
||||||
let hashed_password = hash_password(plain_text_password.to_string()).await?;
|
|
||||||
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
|
||||||
let encrypted_priv_key = crypto::encrypt_password(
|
|
||||||
priv_key_plain_text,
|
|
||||||
plain_text_password.to_string()
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
let recipients = get_recipients(db, &pubkey).await?;
|
|
||||||
let enc_personal_id_number = EncryptedString::new(
|
|
||||||
&personal_id_number,
|
|
||||||
&recipients,
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
let candidate = Mutation::create_candidate(
|
let candidate = Mutation::create_candidate(
|
||||||
db,
|
db,
|
||||||
application_id,
|
enc_personal_id_number,
|
||||||
hashed_password,
|
|
||||||
enc_personal_id_number.to_string(),
|
|
||||||
pubkey,
|
|
||||||
encrypted_priv_key,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
PortfolioService::create_user_dir(candidate.application).await?;
|
||||||
|
|
||||||
|
|
||||||
Ok(candidate)
|
Ok(candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reset_password(
|
|
||||||
admin_private_key: String,
|
|
||||||
db: &DbConn,
|
|
||||||
id: i32,
|
|
||||||
) -> Result<CreateCandidateResponse, ServiceError> {
|
|
||||||
let candidate = Query::find_candidate_by_id(db, id).await?
|
|
||||||
.ok_or(ServiceError::CandidateNotFound)?;
|
|
||||||
let parents = Query::find_candidate_parents(db, &candidate).await?;
|
|
||||||
|
|
||||||
|
|
||||||
let new_password_plain = crypto::random_12_char_string();
|
|
||||||
let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?;
|
|
||||||
|
|
||||||
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
|
||||||
let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text.clone(),
|
|
||||||
new_password_plain.to_string()
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
|
|
||||||
Self::delete_old_sessions(db, &candidate, 0).await?;
|
|
||||||
let candidate = Mutation::update_candidate_password_and_keys(db,
|
|
||||||
candidate,
|
|
||||||
new_password_hash,
|
|
||||||
pubkey.clone(),
|
|
||||||
encrypted_priv_key
|
|
||||||
).await?;
|
|
||||||
|
|
||||||
|
|
||||||
// user might no have filled his details yet, but personal id number is filled from beginning
|
|
||||||
let personal_id_number = EncryptedString::from(candidate.personal_identification_number.clone())
|
|
||||||
.decrypt(&admin_private_key)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let recipients = get_recipients(db, &pubkey).await?;
|
|
||||||
|
|
||||||
let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone()))
|
|
||||||
.decrypt(admin_private_key).await?;
|
|
||||||
let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?;
|
|
||||||
|
|
||||||
Mutation::update_candidate_details(db, candidate, enc_details.candidate).await?;
|
|
||||||
for i in 0..enc_details.parents.len() {
|
|
||||||
Mutation::add_parent_details(db, parents[i].clone(), enc_details.parents[i].clone()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
CreateCandidateResponse {
|
|
||||||
application_id: id,
|
|
||||||
personal_id_number,
|
|
||||||
password: new_password_plain,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_candidate(db: &DbConn, candidate: candidate::Model) -> Result<(), ServiceError> {
|
pub async fn delete_candidate(db: &DbConn, candidate: candidate::Model) -> Result<(), ServiceError> {
|
||||||
PortfolioService::delete_candidate_root(candidate.application).await?;
|
PortfolioService::delete_candidate_root(candidate.application).await?;
|
||||||
|
|
||||||
|
|
@ -166,172 +79,23 @@ impl CandidateService {
|
||||||
})
|
})
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn decrypt_private_key(
|
|
||||||
candidate: candidate::Model,
|
|
||||||
password: String,
|
|
||||||
) -> Result<String, ServiceError> {
|
|
||||||
let private_key_encrypted = candidate.private_key;
|
|
||||||
|
|
||||||
let private_key = crypto::decrypt_password(private_key_encrypted, password).await?;
|
|
||||||
|
|
||||||
Ok(private_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_application_id_valid(application_id: i32) -> bool {
|
|
||||||
let s = &application_id.to_string();
|
|
||||||
if s.len() <= 3 {
|
|
||||||
// TODO: does the field of study prefix have to be exactly 6 digits? VYRESIT PODLE PRIHLASEK!!!
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let field_of_study_prefix = &s[0..3];
|
|
||||||
FIELD_OF_STUDY_PREFIXES.contains(&field_of_study_prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn extend_session_duration_to_14_days(db: &DbConn, session: session::Model) -> Result<session::Model, ServiceError> {
|
|
||||||
let now = chrono::Utc::now().naive_utc();
|
|
||||||
if now >= session.updated_at.checked_add_signed(Duration::days(1)).ok_or(ServiceError::Unauthorized)? {
|
|
||||||
let new_expires_at = now.checked_add_signed(Duration::days(14)).ok_or(ServiceError::Unauthorized)?;
|
|
||||||
|
|
||||||
Ok(Mutation::update_session_expiration(db, session, new_expires_at).await?)
|
|
||||||
} else {
|
|
||||||
Ok(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AuthenticableTrait for CandidateService {
|
|
||||||
type User = candidate::Model;
|
|
||||||
type Session = session::Model;
|
|
||||||
|
|
||||||
async fn login(
|
|
||||||
db: &DbConn,
|
|
||||||
application_id: i32,
|
|
||||||
password: String,
|
|
||||||
ip_addr: String,
|
|
||||||
) -> Result<(String, String), ServiceError> {
|
|
||||||
let candidate = Query::find_candidate_by_id(db, application_id)
|
|
||||||
.await?
|
|
||||||
.ok_or(ServiceError::CandidateNotFound)?;
|
|
||||||
|
|
||||||
let session_id = Self::new_session(db, &candidate, password.clone(), ip_addr).await?;
|
|
||||||
|
|
||||||
let private_key = Self::decrypt_private_key(candidate, password).await?;
|
|
||||||
Ok((session_id, private_key))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<candidate::Model, ServiceError> {
|
|
||||||
let session = Query::find_session_by_uuid(db, session_uuid)
|
|
||||||
.await?
|
|
||||||
.ok_or(ServiceError::Unauthorized)?;
|
|
||||||
|
|
||||||
if !SessionService::is_valid(&session).await? {
|
|
||||||
Mutation::delete_session(db, session.into_active_model()).await?;
|
|
||||||
return Err(ServiceError::ExpiredSession);
|
|
||||||
}
|
|
||||||
// Candidate authenticated
|
|
||||||
|
|
||||||
Self::extend_session_duration_to_14_days(db, session.clone()).await?;
|
|
||||||
|
|
||||||
let candidate = Query::find_candidate_by_id(db, session.candidate_id.unwrap())
|
|
||||||
.await?
|
|
||||||
.ok_or(ServiceError::CandidateNotFound)?;
|
|
||||||
|
|
||||||
Ok(candidate)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn logout(db: &DbConn, session: session::Model) -> Result<(), ServiceError> {
|
|
||||||
Mutation::delete_session(db, session.into_active_model()).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn new_session(
|
|
||||||
db: &DbConn,
|
|
||||||
candidate: &candidate::Model,
|
|
||||||
password: String,
|
|
||||||
ip_addr: String,
|
|
||||||
) -> Result<String, ServiceError> {
|
|
||||||
if !crypto::verify_password(password.clone(), candidate.code.clone()).await? {
|
|
||||||
return Err(ServiceError::InvalidCredentials);
|
|
||||||
}
|
|
||||||
// user is authenticated, generate a new session
|
|
||||||
let random_uuid: Uuid = Uuid::new_v4();
|
|
||||||
|
|
||||||
let session = Mutation::insert_candidate_session(db, random_uuid, candidate.application, ip_addr).await?;
|
|
||||||
|
|
||||||
Self::delete_old_sessions(db, &candidate, 3).await?;
|
|
||||||
|
|
||||||
Ok(session.id.to_string())
|
|
||||||
}
|
|
||||||
async fn delete_old_sessions(
|
|
||||||
db: &DbConn,
|
|
||||||
candidate: &candidate::Model,
|
|
||||||
keep_n_recent: usize,
|
|
||||||
) -> Result<(), ServiceError> {
|
|
||||||
let sessions = Query::find_related_candidate_sessions(db, &candidate)
|
|
||||||
.await?
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.to_owned().into_active_model())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
SessionService::delete_sessions(db, sessions, keep_n_recent).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use sea_orm::DbConn;
|
use sea_orm::DbConn;
|
||||||
|
|
||||||
use crate::models::auth::AuthenticableTrait;
|
|
||||||
use crate::models::candidate_details::tests::assert_all_application_details;
|
use crate::models::candidate_details::tests::assert_all_application_details;
|
||||||
use crate::utils::db::get_memory_sqlite_connection;
|
use crate::utils::db::get_memory_sqlite_connection;
|
||||||
use crate::{crypto, services::candidate_service::CandidateService, Mutation};
|
use crate::{crypto, services::candidate_service::CandidateService, Mutation};
|
||||||
|
|
||||||
use crate::models::candidate_details::EncryptedApplicationDetails;
|
use crate::models::candidate_details::EncryptedApplicationDetails;
|
||||||
use entity::{candidate, parent, admin};
|
use entity::{application, candidate, parent, admin};
|
||||||
|
|
||||||
use crate::services::application_service::ApplicationService;
|
use crate::services::application_service::ApplicationService;
|
||||||
|
|
||||||
const APPLICATION_ID: i32 = 103151;
|
const APPLICATION_ID: i32 = 103151;
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_application_id_validation() {
|
|
||||||
assert!(CandidateService::is_application_id_valid(101_101));
|
|
||||||
assert!(CandidateService::is_application_id_valid(102_107));
|
|
||||||
assert!(CandidateService::is_application_id_valid(103_109));
|
|
||||||
assert!(!CandidateService::is_application_id_valid(104_109));
|
|
||||||
assert!(!CandidateService::is_application_id_valid(100_109));
|
|
||||||
assert!(!CandidateService::is_application_id_valid(201_109));
|
|
||||||
assert!(!CandidateService::is_application_id_valid(101));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_password_reset() {
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
|
||||||
let admin = create_admin(&db).await;
|
|
||||||
let (candidate, _parent) = put_user_data(&db).await;
|
|
||||||
|
|
||||||
let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap();
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_ok()
|
|
||||||
);
|
|
||||||
|
|
||||||
let new_password = CandidateService::reset_password(private_key, &db, candidate.application).await.unwrap().password;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
CandidateService::login(&db, candidate.application, "test".to_string(), "127.0.0.1".to_string()).await.is_err()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
CandidateService::login(&db, candidate.application, new_password, "127.0.0.1".to_string()).await.is_ok()
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_list_candidates() {
|
async fn test_list_candidates() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
@ -346,39 +110,6 @@ pub mod tests {
|
||||||
assert_eq!(candidates.len(), 1);
|
assert_eq!(candidates.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_encrypt_decrypt_private_key_with_passphrase() {
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
|
||||||
|
|
||||||
let plain_text_password = "test".to_string();
|
|
||||||
|
|
||||||
let secret_message = "trnka".to_string();
|
|
||||||
|
|
||||||
let candidate = CandidateService::create(&db, APPLICATION_ID, &plain_text_password, "".to_string())
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Mutation::create_parent(&db, APPLICATION_ID).await.unwrap();
|
|
||||||
|
|
||||||
let encrypted_message =
|
|
||||||
crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key])
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let private_key_plain_text =
|
|
||||||
crypto::decrypt_password(candidate.private_key, plain_text_password)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let decrypted_message =
|
|
||||||
crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(secret_message, decrypted_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
async fn create_admin(db: &DbConn) -> admin::Model {
|
async fn create_admin(db: &DbConn) -> admin::Model {
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
@ -405,27 +136,27 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub async fn put_user_data(db: &DbConn) -> (candidate::Model, Vec<parent::Model>) {
|
pub async fn put_user_data(db: &DbConn) -> (application::Model, candidate::Model, Vec<parent::Model>) {
|
||||||
use crate::models::candidate_details::tests::APPLICATION_DETAILS;
|
use crate::models::candidate_details::tests::APPLICATION_DETAILS;
|
||||||
|
|
||||||
let plain_text_password = "test".to_string();
|
let plain_text_password = "test".to_string();
|
||||||
let (candidate, _parent) = ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(
|
||||||
&db,
|
db,
|
||||||
APPLICATION_ID,
|
APPLICATION_ID,
|
||||||
&plain_text_password,
|
&plain_text_password,
|
||||||
"".to_string(),
|
"0000001111".to_string()
|
||||||
)
|
).await.unwrap();
|
||||||
.await
|
|
||||||
.ok()
|
let candidate= ApplicationService::find_related_candidate(db, application.to_owned()).await.unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let form = APPLICATION_DETAILS.lock().unwrap().clone();
|
let form = APPLICATION_DETAILS.lock().unwrap().clone();
|
||||||
|
|
||||||
let (candidate, parents) = ApplicationService::add_all_details(&db, candidate, &form)
|
let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(
|
(
|
||||||
|
application,
|
||||||
candidate,
|
candidate,
|
||||||
parents,
|
parents,
|
||||||
)
|
)
|
||||||
|
|
@ -434,7 +165,7 @@ pub mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_put_user_data() {
|
async fn test_put_user_data() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (candidate, parents) = put_user_data(&db).await;
|
let (application, candidate, parents) = put_user_data(&db).await;
|
||||||
assert!(candidate.name.is_some());
|
assert!(candidate.name.is_some());
|
||||||
assert!(parents[0].name.is_some());
|
assert!(parents[0].name.is_some());
|
||||||
}
|
}
|
||||||
|
|
@ -443,9 +174,9 @@ pub mod tests {
|
||||||
async fn test_encrypt_decrypt_user_data() {
|
async fn test_encrypt_decrypt_user_data() {
|
||||||
let password = "test".to_string();
|
let password = "test".to_string();
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (enc_candidate, enc_parent) = put_user_data(&db).await;
|
let (application, enc_candidate, enc_parent) = put_user_data(&db).await;
|
||||||
|
|
||||||
let dec_priv_key = crypto::decrypt_password(enc_candidate.private_key.clone(), password)
|
let dec_priv_key = crypto::decrypt_password(application.private_key.clone(), password)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let enc_details = EncryptedApplicationDetails::try_from((&enc_candidate, enc_parent))
|
let enc_details = EncryptedApplicationDetails::try_from((&enc_candidate, enc_parent))
|
||||||
|
|
|
||||||
|
|
@ -93,18 +93,19 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_parent_test() {
|
async fn create_parent_test() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
CandidateService::create(&db, 103100, &"test".to_string(), "".to_string()).await.unwrap();
|
let candidate = CandidateService::create(&db, "".to_string()).await.unwrap();
|
||||||
super::ParentService::create(&db, 103100).await.unwrap();
|
super::ParentService::create(&db, candidate.application).await.unwrap();
|
||||||
super::ParentService::create(&db, 103100).await.unwrap();
|
super::ParentService::create(&db, candidate.application).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
/* #[tokio::test]
|
||||||
async fn add_parent_details_test() {
|
async fn add_parent_details_test() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let plain_text_password = "test".to_string();
|
let plain_text_password = "test".to_string();
|
||||||
let (candidate, _parent) = ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(&db, 103151, &plain_text_password, "0000001111".to_string()).await.unwrap();
|
||||||
|
let (application, candidate, _parent) = ApplicationService::create_candidate_with_parent(
|
||||||
&db,
|
&db,
|
||||||
103101,
|
application,
|
||||||
&plain_text_password,
|
&plain_text_password,
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
)
|
)
|
||||||
|
|
@ -116,11 +117,11 @@ mod tests {
|
||||||
|
|
||||||
let form = APPLICATION_DETAILS_TWO_PARENTS.lock().unwrap().clone();
|
let form = APPLICATION_DETAILS_TWO_PARENTS.lock().unwrap().clone();
|
||||||
|
|
||||||
let (candidate, parents) = ApplicationService::add_all_details(&db, candidate, &form)
|
let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let priv_key = crypto::decrypt_password(candidate.private_key.clone(), plain_text_password).await.unwrap();
|
let priv_key = crypto::decrypt_password(application.private_key.clone(), plain_text_password).await.unwrap();
|
||||||
let dec_details = EncryptedApplicationDetails::try_from((&candidate, parents))
|
let dec_details = EncryptedApplicationDetails::try_from((&candidate, parents))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.decrypt(priv_key)
|
.decrypt(priv_key)
|
||||||
|
|
@ -149,5 +150,5 @@ mod tests {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +268,7 @@ impl PortfolioService {
|
||||||
|
|
||||||
|
|
||||||
/// Move files from cache to final directory and delete cache afterwards
|
/// Move files from cache to final directory and delete cache afterwards
|
||||||
pub async fn submit(candidate: &candidate::Model, db: &DbConn) -> Result<(), ServiceError> {
|
pub async fn submit(public_key: &String, candidate: &candidate::Model, db: &DbConn) -> Result<(), ServiceError> {
|
||||||
let candidate_id = candidate.application;
|
let candidate_id = candidate.application;
|
||||||
let path = Self::get_file_store_path().join(&candidate_id.to_string()).to_path_buf();
|
let path = Self::get_file_store_path().join(&candidate_id.to_string()).to_path_buf();
|
||||||
let cache_path = path.join("cache");
|
let cache_path = path.join("cache");
|
||||||
|
|
@ -307,9 +307,8 @@ impl PortfolioService {
|
||||||
archive.shutdown().await?;
|
archive.shutdown().await?;
|
||||||
|
|
||||||
let admin_public_keys = Query::get_all_admin_public_keys(db).await?;
|
let admin_public_keys = Query::get_all_admin_public_keys(db).await?;
|
||||||
let candidate_public_key = &candidate.public_key;
|
|
||||||
let mut admin_public_keys_refrence: Vec<&str> = admin_public_keys.iter().map(|s| &**s).collect();
|
let mut admin_public_keys_refrence: Vec<&str> = admin_public_keys.iter().map(|s| &**s).collect();
|
||||||
let mut recipients = vec![&**candidate_public_key];
|
let mut recipients = vec![&**public_key];
|
||||||
recipients.append(&mut admin_public_keys_refrence);
|
recipients.append(&mut admin_public_keys_refrence);
|
||||||
|
|
||||||
let final_path = path.join(FileType::PortfolioZip.as_str());
|
let final_path = path.join(FileType::PortfolioZip.as_str());
|
||||||
|
|
@ -420,18 +419,17 @@ mod tests {
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_folder_creation() {
|
async fn test_folder_creation() {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let plain_text_password = "test".to_string();
|
|
||||||
|
|
||||||
let temp_dir = std::env::temp_dir().join("portfolio_test_tempdir").join("create_folder");
|
let temp_dir = std::env::temp_dir().join("portfolio_test_tempdir").join("create_folder");
|
||||||
std::env::set_var("PORTFOLIO_STORE_PATH", temp_dir.to_str().unwrap());
|
std::env::set_var("PORTFOLIO_STORE_PATH", temp_dir.to_str().unwrap());
|
||||||
|
|
||||||
CandidateService::create(&db, APPLICATION_ID, &plain_text_password, "".to_string())
|
let candidate = CandidateService::create(&db, "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(tokio::fs::metadata(temp_dir.join(APPLICATION_ID.to_string())).await.is_ok());
|
assert!(tokio::fs::metadata(temp_dir.join(candidate.application.to_string())).await.is_ok());
|
||||||
assert!(tokio::fs::metadata(temp_dir.join(APPLICATION_ID.to_string()).join("cache")).await.is_ok());
|
assert!(tokio::fs::metadata(temp_dir.join(candidate.application.to_string()).join("cache")).await.is_ok());
|
||||||
|
|
||||||
tokio::fs::remove_dir_all(temp_dir).await.unwrap();
|
tokio::fs::remove_dir_all(temp_dir).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
@ -620,16 +618,16 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_add_portfolio() {
|
async fn test_add_portfolio() {
|
||||||
let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await;
|
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (candidate, _) = put_user_data(&db).await;
|
let (application, candidate, _) = put_user_data(&db).await;
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await;
|
||||||
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
|
||||||
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
|
||||||
|
|
||||||
PortfolioService::submit(&candidate, &db).await.unwrap();
|
PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap();
|
||||||
|
|
||||||
assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
||||||
|
|
||||||
|
|
@ -639,20 +637,20 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_delete_portfolio() {
|
async fn test_delete_portfolio() {
|
||||||
let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await;
|
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (candidate, _) = put_user_data(&db).await;
|
let (application, candidate, _) = put_user_data(&db).await;
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await;
|
||||||
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
|
||||||
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
|
||||||
|
|
||||||
PortfolioService::submit(&candidate, &db).await.unwrap();
|
PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
|
||||||
|
PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap();
|
||||||
|
|
||||||
assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
assert!(tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
||||||
|
|
||||||
PortfolioService::delete_portfolio(APPLICATION_ID).await.unwrap();
|
PortfolioService::delete_portfolio(candidate.application).await.unwrap();
|
||||||
|
|
||||||
assert!(!tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
assert!(!tokio::fs::metadata(application_dir.join("PORTFOLIO.age")).await.is_ok());
|
||||||
|
|
||||||
|
|
@ -662,32 +660,32 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_is_portfolio_submitted() {
|
async fn test_is_portfolio_submitted() {
|
||||||
let (temp_dir, _, _) = create_data_store_temp_dir(APPLICATION_ID).await;
|
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (candidate, _) = put_user_data(&db).await;
|
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
let (application, candidate, _) = put_user_data(&db).await;
|
||||||
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
let (temp_dir, _, _) = create_data_store_temp_dir(candidate.application).await;
|
||||||
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
|
||||||
|
|
||||||
PortfolioService::submit(&candidate, &db).await.unwrap();
|
PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
|
||||||
assert!(PortfolioService::is_portfolio_submitted(APPLICATION_ID).await);
|
PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap();
|
||||||
|
|
||||||
|
assert!(PortfolioService::is_portfolio_submitted(candidate.application).await);
|
||||||
|
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
|
|
||||||
let (temp_dir, application_dir, _) = create_data_store_temp_dir(APPLICATION_ID).await;
|
let (temp_dir, application_dir, _) = create_data_store_temp_dir(candidate.application).await;
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0]).await.unwrap();
|
PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0]).await.unwrap();
|
||||||
|
|
||||||
PortfolioService::submit(&candidate, &db).await.unwrap();
|
PortfolioService::submit(&application.public_key, &candidate, &db).await.unwrap();
|
||||||
|
|
||||||
tokio::fs::remove_file(application_dir.join("PORTFOLIO.age")).await.unwrap();
|
tokio::fs::remove_file(application_dir.join("PORTFOLIO.age")).await.unwrap();
|
||||||
|
|
||||||
assert!(!PortfolioService::is_portfolio_submitted(APPLICATION_ID).await);
|
assert!(!PortfolioService::is_portfolio_submitted(candidate.application).await);
|
||||||
|
|
||||||
clear_data_store_temp_dir(temp_dir).await;
|
clear_data_store_temp_dir(temp_dir).await;
|
||||||
}
|
}
|
||||||
|
|
@ -695,30 +693,30 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_get_portfolio() {
|
async fn test_get_portfolio() {
|
||||||
let (temp_dir, _, _) = create_data_store_temp_dir(APPLICATION_ID).await;
|
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let (candidate, _parent) = put_user_data(&db).await;
|
let (application, candidate, _parent) = put_user_data(&db).await;
|
||||||
|
|
||||||
let private_key = crypto::decrypt_password(candidate.private_key.clone(), "test".to_string())
|
let (temp_dir, _, _) = create_data_store_temp_dir(candidate.application).await;
|
||||||
|
|
||||||
|
let private_key = crypto::decrypt_password(application.private_key.clone(), "test".to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
PortfolioService::add_cover_letter_to_cache(APPLICATION_ID, vec![0])
|
PortfolioService::add_cover_letter_to_cache(candidate.application, vec![0])
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
PortfolioService::add_portfolio_letter_to_cache(APPLICATION_ID, vec![0])
|
PortfolioService::add_portfolio_letter_to_cache(candidate.application, vec![0])
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
PortfolioService::add_portfolio_zip_to_cache(APPLICATION_ID, vec![0])
|
PortfolioService::add_portfolio_zip_to_cache(candidate.application, vec![0])
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
PortfolioService::submit(&candidate, &db)
|
PortfolioService::submit(&application.public_key, &candidate, &db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
PortfolioService::get_portfolio(APPLICATION_ID, private_key)
|
PortfolioService::get_portfolio(candidate.application, private_key)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,30 +42,21 @@ mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto,
|
crypto,
|
||||||
services::{application_service::ApplicationService, candidate_service::CandidateService},
|
services::{application_service::ApplicationService},
|
||||||
utils::db::get_memory_sqlite_connection, models::auth::AuthenticableTrait,
|
utils::db::get_memory_sqlite_connection, models::auth::AuthenticableTrait,
|
||||||
};
|
};
|
||||||
|
const SECRET: &str = "Tajny_kod";
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_create_candidate() {
|
async fn test_create_candidate() {
|
||||||
const SECRET: &str = "Tajny_kod";
|
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let candidate = ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||||
&db,
|
|
||||||
103151,
|
|
||||||
&SECRET.to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
|
|
||||||
assert_eq!(candidate.application, 103151);
|
assert_eq!(application.id.to_owned(), 103151);
|
||||||
assert_ne!(candidate.code, SECRET.to_string());
|
assert_ne!(application.password.to_owned(), SECRET.to_string());
|
||||||
assert!(crypto::verify_password(SECRET.to_string(), candidate.code)
|
assert!(crypto::verify_password(SECRET.to_string(), application.password)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap());
|
.unwrap());
|
||||||
|
|
@ -75,28 +66,20 @@ mod tests {
|
||||||
async fn test_candidate_session_correct_password() {
|
async fn test_candidate_session_correct_password() {
|
||||||
let db = &get_memory_sqlite_connection().await;
|
let db = &get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let candidate = ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||||
db,
|
|
||||||
103151,
|
|
||||||
&"Tajny_kod".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
|
|
||||||
// correct password
|
// correct password
|
||||||
let session = CandidateService::new_session(
|
let session = ApplicationService::new_session(
|
||||||
db,
|
db,
|
||||||
&candidate,
|
&application,
|
||||||
"Tajny_kod".to_string(),
|
SECRET.to_string(),
|
||||||
"127.0.0.1".to_string(),
|
"127.0.0.1".to_string(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// println!("{}", session.err().unwrap().1);
|
// println!("{}", session.err().unwrap().1);
|
||||||
assert!(
|
assert!(
|
||||||
CandidateService::auth(db, Uuid::parse_str(&session).unwrap())
|
ApplicationService::auth(db, Uuid::parse_str(&session).unwrap())
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
);
|
);
|
||||||
|
|
@ -106,20 +89,12 @@ mod tests {
|
||||||
async fn test_candidate_session_incorrect_password() {
|
async fn test_candidate_session_incorrect_password() {
|
||||||
let db = &get_memory_sqlite_connection().await;
|
let db = &get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let candidate_form = ApplicationService::create_candidate_with_parent(
|
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||||
&db,
|
|
||||||
103151,
|
|
||||||
&"Tajny_kod".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.0;
|
|
||||||
|
|
||||||
// incorrect password
|
// incorrect password
|
||||||
assert!(CandidateService::new_session(
|
assert!(ApplicationService::new_session(
|
||||||
db,
|
db,
|
||||||
&candidate_form,
|
&application,
|
||||||
"Spatny_kod".to_string(),
|
"Spatny_kod".to_string(),
|
||||||
"127.0.0.1".to_string()
|
"127.0.0.1".to_string()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use entity::admin_session;
|
use entity::{admin_session, application};
|
||||||
use sea_orm::DbConn;
|
use sea_orm::DbConn;
|
||||||
|
|
||||||
use crate::Query;
|
use crate::Query;
|
||||||
|
|
@ -22,15 +22,17 @@ pub async fn get_memory_sqlite_connection() -> sea_orm::DbConn {
|
||||||
|
|
||||||
let schema = Schema::new(DbBackend::Sqlite);
|
let schema = Schema::new(DbBackend::Sqlite);
|
||||||
let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity);
|
let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity);
|
||||||
let stmt2: TableCreateStatement = schema.create_table_from_entity(admin::Entity);
|
let stmt2: TableCreateStatement = schema.create_table_from_entity(application::Entity);
|
||||||
let stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity);
|
let stmt3: TableCreateStatement = schema.create_table_from_entity(session::Entity);
|
||||||
let stmt4: TableCreateStatement = schema.create_table_from_entity(parent::Entity);
|
let stmt4: TableCreateStatement = schema.create_table_from_entity(admin::Entity);
|
||||||
let stmt5: TableCreateStatement = schema.create_table_from_entity(admin_session::Entity);
|
let stmt5: TableCreateStatement = schema.create_table_from_entity(admin_session::Entity);
|
||||||
|
let stmt6: TableCreateStatement = schema.create_table_from_entity(parent::Entity);
|
||||||
db.execute(db.get_database_backend().build(&stmt)).await.unwrap();
|
db.execute(db.get_database_backend().build(&stmt)).await.unwrap();
|
||||||
db.execute(db.get_database_backend().build(&stmt2)).await.unwrap();
|
db.execute(db.get_database_backend().build(&stmt2)).await.unwrap();
|
||||||
db.execute(db.get_database_backend().build(&stmt3)).await.unwrap();
|
db.execute(db.get_database_backend().build(&stmt3)).await.unwrap();
|
||||||
db.execute(db.get_database_backend().build(&stmt4)).await.unwrap();
|
db.execute(db.get_database_backend().build(&stmt4)).await.unwrap();
|
||||||
db.execute(db.get_database_backend().build(&stmt5)).await.unwrap();
|
db.execute(db.get_database_backend().build(&stmt5)).await.unwrap();
|
||||||
|
db.execute(db.get_database_backend().build(&stmt6)).await.unwrap();
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
45
entity/src/application.rs
Normal file
45
entity/src/application.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
#[sea_orm(table_name = "application")]
|
||||||
|
pub struct Model {
|
||||||
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
|
pub id: i32,
|
||||||
|
pub candidate_id: i32,
|
||||||
|
pub password: String,
|
||||||
|
pub public_key: String,
|
||||||
|
pub private_key: String,
|
||||||
|
pub personal_id_number: String,
|
||||||
|
pub created_at: DateTime,
|
||||||
|
pub updated_at: DateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
pub enum Relation {
|
||||||
|
#[sea_orm(
|
||||||
|
belongs_to = "super::candidate::Entity",
|
||||||
|
from = "Column::CandidateId",
|
||||||
|
to = "super::candidate::Column::Application",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
|
)]
|
||||||
|
Candidate,
|
||||||
|
#[sea_orm(has_many = "super::session::Entity")]
|
||||||
|
Session,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::candidate::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Candidate.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Related<super::session::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Session.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
@ -5,9 +5,8 @@ use sea_orm::entity::prelude::*;
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "candidate")]
|
#[sea_orm(table_name = "candidate")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key)]
|
||||||
pub application: i32,
|
pub application: i32,
|
||||||
pub code: String,
|
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub surname: Option<String>,
|
pub surname: Option<String>,
|
||||||
pub birth_surname: Option<String>,
|
pub birth_surname: Option<String>,
|
||||||
|
|
@ -22,23 +21,21 @@ pub struct Model {
|
||||||
pub personal_identification_number: String,
|
pub personal_identification_number: String,
|
||||||
pub school_name: Option<String>,
|
pub school_name: Option<String>,
|
||||||
pub health_insurance: Option<String>,
|
pub health_insurance: Option<String>,
|
||||||
pub public_key: String,
|
|
||||||
pub private_key: String,
|
|
||||||
pub created_at: DateTime,
|
pub created_at: DateTime,
|
||||||
pub updated_at: DateTime,
|
pub updated_at: DateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
#[sea_orm(has_many = "super::session::Entity")]
|
#[sea_orm(has_many = "super::application::Entity")]
|
||||||
Session,
|
Application,
|
||||||
#[sea_orm(has_many = "super::parent::Entity")]
|
#[sea_orm(has_many = "super::parent::Entity")]
|
||||||
Parent,
|
Parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Related<super::session::Entity> for Entity {
|
impl Related<super::application::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Session.def()
|
Relation::Application.def()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ pub mod parent;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
pub mod admin_session;
|
pub mod admin_session;
|
||||||
pub mod session_trait;
|
pub mod session_trait;
|
||||||
|
pub mod application;
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod prelude;
|
||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod admin_session;
|
pub mod admin_session;
|
||||||
|
pub mod application;
|
||||||
pub mod candidate;
|
pub mod candidate;
|
||||||
pub mod parent;
|
pub mod parent;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
pub use super::admin::Entity as Admin;
|
pub use super::admin::Entity as Admin;
|
||||||
pub use super::admin_session::Entity as AdminSession;
|
pub use super::admin_session::Entity as AdminSession;
|
||||||
|
pub use super::application::Entity as Application;
|
||||||
pub use super::candidate::Entity as Candidate;
|
pub use super::candidate::Entity as Candidate;
|
||||||
pub use super::parent::Entity as Parent;
|
pub use super::parent::Entity as Parent;
|
||||||
pub use super::session::Entity as Session;
|
pub use super::session::Entity as Session;
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,18 @@ pub struct Model {
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::candidate::Entity",
|
belongs_to = "super::application::Entity",
|
||||||
from = "Column::CandidateId",
|
from = "Column::CandidateId",
|
||||||
to = "super::candidate::Column::Application",
|
to = "super::application::Column::Id",
|
||||||
on_update = "Cascade",
|
on_update = "Cascade",
|
||||||
on_delete = "Cascade"
|
on_delete = "Cascade"
|
||||||
)]
|
)]
|
||||||
Candidate,
|
Application,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Related<super::candidate::Entity> for Entity {
|
impl Related<super::application::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Candidate.def()
|
Relation::Application.def()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ mod m20221027_194728_session_create_user_fk;
|
||||||
mod m20221028_194728_session_create_admin_fk;
|
mod m20221028_194728_session_create_admin_fk;
|
||||||
mod m20221112_112212_create_parent_candidate_fk;
|
mod m20221112_112212_create_parent_candidate_fk;
|
||||||
mod m20221221_162232_create_admin_session;
|
mod m20221221_162232_create_admin_session;
|
||||||
|
mod m20230114_114628_create_application;
|
||||||
|
mod m20230114_114826_create_application_candidate_fk;
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
@ -20,6 +22,7 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20221024_124701_create_parent::Migration),
|
Box::new(m20221024_124701_create_parent::Migration),
|
||||||
Box::new(m20221025_154422_create_session::Migration),
|
Box::new(m20221025_154422_create_session::Migration),
|
||||||
Box::new(m20221221_162232_create_admin_session::Migration),
|
Box::new(m20221221_162232_create_admin_session::Migration),
|
||||||
|
Box::new(m20230114_114628_create_application::Migration),
|
||||||
];
|
];
|
||||||
|
|
||||||
if cfg!(debug_assertions) || cfg!(test) {
|
if cfg!(debug_assertions) || cfg!(test) {
|
||||||
|
|
@ -36,6 +39,7 @@ impl MigratorTrait for Migrator {
|
||||||
migrations.push(Box::new(
|
migrations.push(Box::new(
|
||||||
m20221028_194728_session_create_admin_fk::Migration,
|
m20221028_194728_session_create_admin_fk::Migration,
|
||||||
));
|
));
|
||||||
|
migrations.push(Box::new(m20230114_114826_create_application_candidate_fk::Migration));
|
||||||
}
|
}
|
||||||
|
|
||||||
migrations
|
migrations
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@ impl MigrationTrait for Migration {
|
||||||
.integer()
|
.integer()
|
||||||
.not_null()
|
.not_null()
|
||||||
.primary_key()
|
.primary_key()
|
||||||
.unique_key(),
|
.auto_increment(),
|
||||||
)
|
)
|
||||||
.col(ColumnDef::new(Candidate::Code).string().not_null())
|
|
||||||
.col(ColumnDef::new(Candidate::Name).string())
|
.col(ColumnDef::new(Candidate::Name).string())
|
||||||
.col(ColumnDef::new(Candidate::Surname).string())
|
.col(ColumnDef::new(Candidate::Surname).string())
|
||||||
.col(ColumnDef::new(Candidate::BirthSurname).string())
|
.col(ColumnDef::new(Candidate::BirthSurname).string())
|
||||||
|
|
@ -33,8 +32,6 @@ impl MigrationTrait for Migration {
|
||||||
.col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string().not_null())
|
.col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string().not_null())
|
||||||
.col(ColumnDef::new(Candidate::SchoolName).string())
|
.col(ColumnDef::new(Candidate::SchoolName).string())
|
||||||
.col(ColumnDef::new(Candidate::HealthInsurance).string())
|
.col(ColumnDef::new(Candidate::HealthInsurance).string())
|
||||||
.col(ColumnDef::new(Candidate::PublicKey).string().not_null())
|
|
||||||
.col(ColumnDef::new(Candidate::PrivateKey).string().not_null())
|
|
||||||
.col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null())
|
.col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null())
|
||||||
.col(ColumnDef::new(Candidate::UpdatedAt).date_time().not_null())
|
.col(ColumnDef::new(Candidate::UpdatedAt).date_time().not_null())
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|
|
@ -53,7 +50,6 @@ impl MigrationTrait for Migration {
|
||||||
pub enum Candidate {
|
pub enum Candidate {
|
||||||
Table,
|
Table,
|
||||||
Application,
|
Application,
|
||||||
Code,
|
|
||||||
Name,
|
Name,
|
||||||
Surname,
|
Surname,
|
||||||
BirthSurname,
|
BirthSurname,
|
||||||
|
|
@ -68,8 +64,6 @@ pub enum Candidate {
|
||||||
PersonalIdentificationNumber,
|
PersonalIdentificationNumber,
|
||||||
SchoolName,
|
SchoolName,
|
||||||
HealthInsurance,
|
HealthInsurance,
|
||||||
PublicKey,
|
|
||||||
PrivateKey,
|
|
||||||
CreatedAt,
|
CreatedAt,
|
||||||
UpdatedAt,
|
UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use sea_orm_migration::prelude::*;
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
use crate::{m20221025_154422_create_session::Session, m20221024_121621_create_candidate::Candidate};
|
use crate::{m20221025_154422_create_session::Session, m20230114_114628_create_application::Application};
|
||||||
|
|
||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
pub struct Migration;
|
pub struct Migration;
|
||||||
|
|
@ -11,7 +11,7 @@ impl MigrationTrait for Migration {
|
||||||
manager.create_foreign_key(ForeignKey::create()
|
manager.create_foreign_key(ForeignKey::create()
|
||||||
.name("user_fk")
|
.name("user_fk")
|
||||||
.from(Session::Table, Session::CandidateId)
|
.from(Session::Table, Session::CandidateId)
|
||||||
.to(Candidate::Table, Candidate::Application)
|
.to(Application::Table, Application::Id)
|
||||||
.on_delete(ForeignKeyAction::Cascade)
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
.on_update(ForeignKeyAction::Cascade)
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
.to_owned()).await
|
.to_owned()).await
|
||||||
|
|
|
||||||
51
migration/src/m20230114_114628_create_application.rs
Normal file
51
migration/src/m20230114_114628_create_application.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(Application::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Application::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Application::CandidateId).integer().not_null())
|
||||||
|
.col(ColumnDef::new(Application::Password).string().not_null())
|
||||||
|
.col(ColumnDef::new(Application::PublicKey).string().not_null())
|
||||||
|
.col(ColumnDef::new(Application::PrivateKey).string().not_null())
|
||||||
|
.col(ColumnDef::new(Application::PersonalIdNumber).string().not_null())
|
||||||
|
.col(ColumnDef::new(Application::CreatedAt).date_time().not_null())
|
||||||
|
.col(ColumnDef::new(Application::UpdatedAt).date_time().not_null())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Application::Table).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Learn more at https://docs.rs/sea-query#iden
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum Application {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Password,
|
||||||
|
PersonalIdNumber,
|
||||||
|
PublicKey,
|
||||||
|
PrivateKey,
|
||||||
|
CandidateId,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
use crate::{m20221024_121621_create_candidate::Candidate, m20230114_114628_create_application::Application};
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager.create_foreign_key(ForeignKey::create()
|
||||||
|
.name("candidate_fk")
|
||||||
|
.from(Application::Table, Application::CandidateId)
|
||||||
|
.to(Candidate::Table, Candidate::Application)
|
||||||
|
.on_delete(ForeignKeyAction::Cascade)
|
||||||
|
.on_update(ForeignKeyAction::Cascade)
|
||||||
|
.to_owned()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager.drop_foreign_key(ForeignKey::drop()
|
||||||
|
.name("candidate_fk")
|
||||||
|
.table(Application::Table)
|
||||||
|
.to_owned()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue