mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-24 17:11:49 +00:00
Merge pull request #40 from EETagent/parent_details
Parent details, massive refactoring
This commit is contained in:
commit
72ae61637f
25 changed files with 498 additions and 348 deletions
|
|
@ -51,7 +51,7 @@ impl<'r> FromRequest<'r> for AdminAuth {
|
||||||
match session {
|
match session {
|
||||||
Ok(model) => Outcome::Success(AdminAuth(model, private_key.to_string())),
|
Ok(model) => Outcome::Success(AdminAuth(model, private_key.to_string())),
|
||||||
Err(e) => Outcome::Failure(
|
Err(e) => Outcome::Failure(
|
||||||
(Status::from_code(e.code()).unwrap_or(Status::InternalServerError), None)
|
(Status::from_code(e.code()).unwrap_or(Status::Unauthorized), None)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::net::SocketAddr;
|
||||||
|
|
||||||
use portfolio_core::{
|
use portfolio_core::{
|
||||||
crypto::random_8_char_string,
|
crypto::random_8_char_string,
|
||||||
services::{admin_service::AdminService, candidate_service::CandidateService},
|
services::{admin_service::AdminService, candidate_service::CandidateService, application_service::ApplicationService},
|
||||||
};
|
};
|
||||||
use requests::{AdminLoginRequest, RegisterRequest};
|
use requests::{AdminLoginRequest, RegisterRequest};
|
||||||
use rocket::http::{Cookie, Status, CookieJar};
|
use rocket::http::{Cookie, Status, CookieJar};
|
||||||
|
|
@ -75,22 +75,14 @@ pub async fn create_candidate(
|
||||||
|
|
||||||
let plain_text_password = random_8_char_string();
|
let plain_text_password = random_8_char_string();
|
||||||
|
|
||||||
let candidate = CandidateService::create(
|
ApplicationService::create_candidate_with_parent(
|
||||||
db,
|
db,
|
||||||
form.application_id,
|
form.application_id,
|
||||||
&plain_text_password,
|
&plain_text_password,
|
||||||
form.personal_id_number,
|
form.personal_id_number,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.map_err(|e| Custom(Status::InternalServerError, e.to_string()))?;
|
||||||
if candidate.is_err() {
|
|
||||||
// TODO cleanup
|
|
||||||
let e = candidate.err().unwrap();
|
|
||||||
return Err(Custom(
|
|
||||||
Status::from_code(e.code()).unwrap_or_default(),
|
|
||||||
e.message(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(plain_text_password)
|
Ok(plain_text_password)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use portfolio_core::services::candidate_service::{CandidateService, UserDetails};
|
use portfolio_core::candidate_details::ApplicationDetails;
|
||||||
|
use portfolio_core::services::application_service::ApplicationService;
|
||||||
|
use portfolio_core::services::candidate_service::{CandidateService};
|
||||||
use requests::LoginRequest;
|
use requests::LoginRequest;
|
||||||
use rocket::http::{Cookie, CookieJar, Status};
|
use rocket::http::{Cookie, CookieJar, Status};
|
||||||
use rocket::response::status::Custom;
|
use rocket::response::status::Custom;
|
||||||
|
|
@ -59,18 +61,18 @@ pub async fn whoami(session: CandidateAuth) -> Result<String, Custom<String>> {
|
||||||
#[post("/details", data = "<details>")]
|
#[post("/details", data = "<details>")]
|
||||||
pub async fn fill_details(
|
pub async fn fill_details(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
details: Json<UserDetails>,
|
details: Json<ApplicationDetails>,
|
||||||
session: CandidateAuth,
|
session: CandidateAuth,
|
||||||
) -> Result<String, Custom<String>> {
|
) -> Result<String, 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 candidate: entity::candidate::Model = session.into(); // TODO: don't return candidate from session
|
||||||
|
|
||||||
let candidate = CandidateService::add_user_details(db, candidate, form).await;
|
let candidate_parent = ApplicationService::add_all_details(db, candidate.application, form).await;
|
||||||
|
|
||||||
if candidate.is_err() {
|
if candidate_parent.is_err() {
|
||||||
// TODO cleanup
|
// TODO cleanup
|
||||||
let e = candidate.err().unwrap();
|
let e = candidate_parent.err().unwrap();
|
||||||
return Err(Custom(
|
return Err(Custom(
|
||||||
Status::from_code(e.code()).unwrap_or_default(),
|
Status::from_code(e.code()).unwrap_or_default(),
|
||||||
e.message(),
|
e.message(),
|
||||||
|
|
@ -85,13 +87,13 @@ pub async fn get_details(
|
||||||
conn: Connection<'_, Db>,
|
conn: Connection<'_, Db>,
|
||||||
password_form: Json<PasswordRequest>,
|
password_form: Json<PasswordRequest>,
|
||||||
session: CandidateAuth,
|
session: CandidateAuth,
|
||||||
) -> Result<Json<UserDetails>, Custom<String>> {
|
) -> Result<Json<ApplicationDetails>, Custom<String>> {
|
||||||
let db = conn.into_inner();
|
let db = conn.into_inner();
|
||||||
let candidate: entity::candidate::Model = session.into();
|
let candidate: entity::candidate::Model = session.into();
|
||||||
let password = password_form.password.clone();
|
let password = password_form.password.clone();
|
||||||
|
|
||||||
// let handle = tokio::spawn(async move {
|
// let handle = tokio::spawn(async move {
|
||||||
let details = CandidateService::decrypt_details(db, candidate.application, password)
|
let details = ApplicationService::decrypt_all_details(db, candidate.application, password)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()));
|
.map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()));
|
||||||
|
|
||||||
|
|
|
||||||
203
core/src/candidate_details.rs
Normal file
203
core/src/candidate_details.rs
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
use chrono::{NaiveDate};
|
||||||
|
use entity::{candidate, parent};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::{error::ServiceError, crypto};
|
||||||
|
|
||||||
|
pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d";
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EncryptedString(String);
|
||||||
|
|
||||||
|
impl EncryptedString {
|
||||||
|
pub async fn new(s: &str, recipients: &Vec<&str>) -> Result<Self, ServiceError> {
|
||||||
|
match crypto::encrypt_password_with_recipients(&s, &recipients).await{
|
||||||
|
Ok(encrypted) => Ok(Self(encrypted)),
|
||||||
|
Err(_) => Err(ServiceError::CryptoEncryptFailed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt(&self, private_key: &String) -> Result<String, ServiceError> {
|
||||||
|
match crypto::decrypt_password_with_private_key(&self.0, private_key).await {
|
||||||
|
Ok(decrypted) => Ok(decrypted),
|
||||||
|
Err(_) => Err(ServiceError::CryptoDecryptFailed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for EncryptedString {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Option<String>> for EncryptedString {
|
||||||
|
type Error = ServiceError;
|
||||||
|
|
||||||
|
fn try_from(s: Option<String>) -> Result<Self, Self::Error> {
|
||||||
|
match s {
|
||||||
|
Some(s) => Ok(Self(s)),
|
||||||
|
None => Err(ServiceError::CandidateDetailsNotSet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Option<NaiveDate>> for EncryptedString { // TODO: take a look at this
|
||||||
|
type Error = ServiceError;
|
||||||
|
|
||||||
|
fn try_from(d: Option<NaiveDate>) -> Result<Self, Self::Error> {
|
||||||
|
match d {
|
||||||
|
Some(d) => Ok(Self(d.to_string())),
|
||||||
|
None => Err(ServiceError::CandidateDetailsNotSet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EncryptedApplicationDetails {
|
||||||
|
// Candidate
|
||||||
|
pub name: EncryptedString,
|
||||||
|
pub surname: EncryptedString,
|
||||||
|
pub birthplace: EncryptedString,
|
||||||
|
pub birthdate: EncryptedString,
|
||||||
|
pub address: EncryptedString,
|
||||||
|
pub telephone: EncryptedString,
|
||||||
|
pub citizenship: EncryptedString,
|
||||||
|
pub email: EncryptedString,
|
||||||
|
pub sex: EncryptedString,
|
||||||
|
pub study: EncryptedString,
|
||||||
|
|
||||||
|
// Parent
|
||||||
|
pub parent_name: EncryptedString,
|
||||||
|
pub parent_surname: EncryptedString,
|
||||||
|
pub parent_telephone: EncryptedString,
|
||||||
|
pub parent_email: EncryptedString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncryptedApplicationDetails {
|
||||||
|
pub async fn new(form: ApplicationDetails, recipients: Vec<&str>) -> Result<EncryptedApplicationDetails, ServiceError> {
|
||||||
|
let birthdate_str = form.birthdate.format(NAIVE_DATE_FMT).to_string();
|
||||||
|
let d = tokio::try_join!(
|
||||||
|
EncryptedString::new(&form.name, &recipients),
|
||||||
|
EncryptedString::new(&form.surname, &recipients),
|
||||||
|
EncryptedString::new(&form.birthplace, &recipients),
|
||||||
|
EncryptedString::new(&birthdate_str, &recipients),
|
||||||
|
EncryptedString::new(&form.address, &recipients),
|
||||||
|
EncryptedString::new(&form.telephone, &recipients),
|
||||||
|
EncryptedString::new(&form.citizenship, &recipients),
|
||||||
|
EncryptedString::new(&form.email, &recipients),
|
||||||
|
EncryptedString::new(&form.sex, &recipients),
|
||||||
|
EncryptedString::new(&form.study, &recipients),
|
||||||
|
|
||||||
|
EncryptedString::new(&form.parent_name, &recipients),
|
||||||
|
EncryptedString::new(&form.parent_surname, &recipients),
|
||||||
|
EncryptedString::new(&form.parent_telephone, &recipients),
|
||||||
|
EncryptedString::new(&form.parent_email, &recipients),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(EncryptedApplicationDetails {
|
||||||
|
name: d.0,
|
||||||
|
surname: d.1,
|
||||||
|
birthplace: d.2,
|
||||||
|
birthdate: d.3,
|
||||||
|
address: d.4,
|
||||||
|
telephone: d.5,
|
||||||
|
citizenship: d.6,
|
||||||
|
email: d.7,
|
||||||
|
sex: d.8,
|
||||||
|
study: d.9,
|
||||||
|
|
||||||
|
parent_name: d.10,
|
||||||
|
parent_surname: d.11,
|
||||||
|
parent_telephone: d.12,
|
||||||
|
parent_email: d.13,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt(self, priv_key: String) -> Result<ApplicationDetails, ServiceError> {
|
||||||
|
let d = tokio::try_join!(
|
||||||
|
self.name.decrypt(&priv_key), // 0
|
||||||
|
self.surname.decrypt(&priv_key), // 1
|
||||||
|
self.birthplace.decrypt(&priv_key), // 2
|
||||||
|
self.birthdate.decrypt(&priv_key), // 3
|
||||||
|
self.address.decrypt(&priv_key), // 4
|
||||||
|
self.telephone.decrypt(&priv_key), // 5
|
||||||
|
self.citizenship.decrypt(&priv_key), // 6
|
||||||
|
self.email.decrypt(&priv_key), // 7
|
||||||
|
self.sex.decrypt(&priv_key), // 8
|
||||||
|
self.study.decrypt(&priv_key), // 9
|
||||||
|
|
||||||
|
self.parent_name.decrypt(&priv_key),
|
||||||
|
self.parent_surname.decrypt(&priv_key),
|
||||||
|
self.parent_telephone.decrypt(&priv_key),
|
||||||
|
self.parent_email.decrypt(&priv_key),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(ApplicationDetails {
|
||||||
|
name: d.0,
|
||||||
|
surname: d.1,
|
||||||
|
birthplace: d.2,
|
||||||
|
birthdate: NaiveDate::parse_from_str(&d.3, NAIVE_DATE_FMT).unwrap(), // TODO
|
||||||
|
address: d.4,
|
||||||
|
telephone: d.5,
|
||||||
|
citizenship: d.6,
|
||||||
|
email: d.7,
|
||||||
|
sex: d.8,
|
||||||
|
study: d.9,
|
||||||
|
|
||||||
|
parent_name: d.10,
|
||||||
|
parent_surname: d.11,
|
||||||
|
parent_telephone: d.12,
|
||||||
|
parent_email: d.13,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(candidate::Model, parent::Model)> for EncryptedApplicationDetails {
|
||||||
|
type Error = ServiceError;
|
||||||
|
|
||||||
|
fn try_from((candidate, parent): (candidate::Model, parent::Model)) -> Result<Self, Self::Error> {
|
||||||
|
Ok(EncryptedApplicationDetails {
|
||||||
|
name: EncryptedString::try_from(candidate.name)?,
|
||||||
|
surname: EncryptedString::try_from(candidate.surname)?,
|
||||||
|
birthplace: EncryptedString::try_from(candidate.birthplace)?,
|
||||||
|
birthdate: EncryptedString::try_from(candidate.birthdate)?,
|
||||||
|
address: EncryptedString::try_from(candidate.address)?,
|
||||||
|
telephone: EncryptedString::try_from(candidate.telephone)?,
|
||||||
|
citizenship: EncryptedString::try_from(candidate.citizenship)?,
|
||||||
|
email: EncryptedString::try_from(candidate.email)?,
|
||||||
|
sex: EncryptedString::try_from(candidate.sex)?,
|
||||||
|
study: EncryptedString::try_from(candidate.study)?,
|
||||||
|
|
||||||
|
parent_name: EncryptedString::try_from(parent.name)?,
|
||||||
|
parent_surname: EncryptedString::try_from(parent.surname)?,
|
||||||
|
parent_telephone: EncryptedString::try_from(parent.telephone)?,
|
||||||
|
parent_email: EncryptedString::try_from(parent.email)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ApplicationDetails {
|
||||||
|
// Candidate
|
||||||
|
pub name: String,
|
||||||
|
pub surname: String,
|
||||||
|
pub birthplace: String,
|
||||||
|
pub birthdate: NaiveDate, // TODO: User NaiveDate or String?
|
||||||
|
pub address: String,
|
||||||
|
pub telephone: String,
|
||||||
|
pub citizenship: String,
|
||||||
|
pub email: String,
|
||||||
|
pub sex: String,
|
||||||
|
pub study: String,
|
||||||
|
|
||||||
|
// Parent
|
||||||
|
pub parent_name: String,
|
||||||
|
pub parent_surname: String,
|
||||||
|
pub parent_telephone: String,
|
||||||
|
pub parent_email: String,
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Mutation, services::candidate_service::EncryptedUserDetails};
|
use crate::{Mutation, candidate_details::{EncryptedApplicationDetails}};
|
||||||
|
|
||||||
use ::entity::candidate::{self};
|
use ::entity::candidate::{self};
|
||||||
use sea_orm::{*};
|
use sea_orm::{*};
|
||||||
|
|
@ -29,18 +29,19 @@ impl Mutation {
|
||||||
pub async fn add_candidate_details(
|
pub async fn add_candidate_details(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
user: candidate::Model,
|
user: candidate::Model,
|
||||||
enc_details: EncryptedUserDetails,
|
enc_details: EncryptedApplicationDetails,
|
||||||
) -> Result<candidate::Model, sea_orm::DbErr> {
|
) -> Result<candidate::Model, sea_orm::DbErr> {
|
||||||
let mut user: candidate::ActiveModel = user.into();
|
let mut user: candidate::ActiveModel = user.into();
|
||||||
user.name = Set(Some(enc_details.name));
|
user.name = Set(Some(enc_details.name.into()));
|
||||||
user.surname = Set(Some(enc_details.surname));
|
user.surname = Set(Some(enc_details.surname.into()));
|
||||||
user.birthplace = Set(Some(enc_details.birthplace));
|
user.birthplace = Set(Some(enc_details.birthplace.into()));
|
||||||
user.address = Set(Some(enc_details.address));
|
user.birthdate = Set(Some(enc_details.birthdate.into()));
|
||||||
user.telephone = Set(Some(enc_details.telephone));
|
user.address = Set(Some(enc_details.address.into()));
|
||||||
user.citizenship = Set(Some(enc_details.citizenship));
|
user.telephone = Set(Some(enc_details.telephone.into()));
|
||||||
user.email = Set(Some(enc_details.email));
|
user.citizenship = Set(Some(enc_details.citizenship.into()));
|
||||||
user.sex = Set(Some(enc_details.sex));
|
user.email = Set(Some(enc_details.email.into()));
|
||||||
user.study = Set(Some(enc_details.study));
|
user.sex = Set(Some(enc_details.sex.into()));
|
||||||
|
user.study = Set(Some(enc_details.study.into()));
|
||||||
|
|
||||||
user.updated_at = Set(chrono::offset::Local::now().naive_local());
|
user.updated_at = Set(chrono::offset::Local::now().naive_local());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Mutation;
|
use crate::{Mutation, candidate_details::EncryptedApplicationDetails};
|
||||||
|
|
||||||
use ::entity::parent::{self, Model};
|
use ::entity::parent::{self, Model};
|
||||||
use sea_orm::*;
|
use sea_orm::*;
|
||||||
|
|
@ -17,17 +17,14 @@ impl Mutation {
|
||||||
|
|
||||||
pub async fn add_parent_details(
|
pub async fn add_parent_details(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
user: Model,
|
parent: Model,
|
||||||
name: String,
|
enc_details: EncryptedApplicationDetails, // TODO: use seperate struct??
|
||||||
surname: String,
|
|
||||||
telephone: String,
|
|
||||||
email: String,
|
|
||||||
) -> Result<Model, sea_orm::DbErr> {
|
) -> Result<Model, sea_orm::DbErr> {
|
||||||
let mut user: parent::ActiveModel = user.into();
|
let mut user: parent::ActiveModel = parent.into();
|
||||||
user.name = Set(Some(name));
|
user.name = Set(Some(enc_details.parent_name.into()));
|
||||||
user.surname = Set(Some(surname));
|
user.surname = Set(Some(enc_details.parent_surname.into()));
|
||||||
user.telephone = Set(Some(telephone));
|
user.telephone = Set(Some(enc_details.parent_telephone.into()));
|
||||||
user.email = Set(Some(email));
|
user.email = Set(Some(enc_details.parent_email.into()));
|
||||||
|
|
||||||
user.updated_at = Set(chrono::offset::Local::now().naive_local());
|
user.updated_at = Set(chrono::offset::Local::now().naive_local());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ pub struct Query;
|
||||||
|
|
||||||
pub mod candidate;
|
pub mod candidate;
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
pub mod parent;
|
||||||
17
core/src/database/query/parent.rs
Normal file
17
core/src/database/query/parent.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
use entity::parent::Model;
|
||||||
|
use entity::parent::Entity;
|
||||||
|
use sea_orm::{DbConn, DbErr};
|
||||||
|
use sea_orm::EntityTrait;
|
||||||
|
|
||||||
|
use crate::Query;
|
||||||
|
|
||||||
|
impl Query {
|
||||||
|
pub async fn find_parent_by_id(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
) -> Result<Option<Model>, DbErr> {
|
||||||
|
|
||||||
|
Entity::find_by_id(application_id).one(db).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,8 @@ pub enum ServiceError {
|
||||||
ExpiredSession,
|
ExpiredSession,
|
||||||
JwtError,
|
JwtError,
|
||||||
UserAlreadyExists,
|
UserAlreadyExists,
|
||||||
UserNotFound,
|
CandidateNotFound,
|
||||||
|
ParentNotFound,
|
||||||
DbError,
|
DbError,
|
||||||
UserNotFoundByJwtId,
|
UserNotFoundByJwtId,
|
||||||
UserNotFoundBySessionId,
|
UserNotFoundBySessionId,
|
||||||
|
|
@ -24,7 +25,8 @@ impl ServiceError {
|
||||||
ServiceError::ExpiredSession => (401, "Session expired, please login again".to_string()),
|
ServiceError::ExpiredSession => (401, "Session expired, please login again".to_string()),
|
||||||
ServiceError::JwtError => (500, "Error while encoding JWT".to_string()),
|
ServiceError::JwtError => (500, "Error while encoding JWT".to_string()),
|
||||||
ServiceError::UserAlreadyExists => (409, "User already exists".to_string()),
|
ServiceError::UserAlreadyExists => (409, "User already exists".to_string()),
|
||||||
ServiceError::UserNotFound => (404, "User not found".to_string()),
|
ServiceError::CandidateNotFound => (404, "User not found".to_string()),
|
||||||
|
ServiceError::ParentNotFound => (500, "Parent not found".to_string()),
|
||||||
ServiceError::DbError => (500, "Database error".to_string()),
|
ServiceError::DbError => (500, "Database error".to_string()),
|
||||||
ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()),
|
ServiceError::UserNotFoundByJwtId => (500, "User not found, please contact technical support".to_string()),
|
||||||
ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()),
|
ServiceError::UserNotFoundBySessionId => (500, "User not found, please contact technical support".to_string()),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ pub mod crypto;
|
||||||
pub mod filetype;
|
pub mod filetype;
|
||||||
pub mod services;
|
pub mod services;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod candidate_details;
|
||||||
|
|
||||||
pub use database::mutation::*;
|
pub use database::mutation::*;
|
||||||
pub use database::query::*;
|
pub use database::query::*;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ impl AdminService {
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(admin) = admin else {
|
let Some(admin) = admin else {
|
||||||
return Err(ServiceError::UserNotFound);
|
return Err(ServiceError::CandidateNotFound);
|
||||||
};
|
};
|
||||||
|
|
||||||
let private_key_encrypted = admin.private_key;
|
let private_key_encrypted = admin.private_key;
|
||||||
|
|
@ -55,7 +55,7 @@ impl AdminService {
|
||||||
match SessionService::auth_user_session(db, session_uuid).await {
|
match SessionService::auth_user_session(db, session_uuid).await {
|
||||||
Ok(user) => match user {
|
Ok(user) => match user {
|
||||||
AdminUser::Admin(admin) => Ok(admin),
|
AdminUser::Admin(admin) => Ok(admin),
|
||||||
AdminUser::User(_) => Err(ServiceError::DbError),
|
AdminUser::Candidate(_) => Err(ServiceError::DbError),
|
||||||
},
|
},
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
95
core/src/services/application_service.rs
Normal file
95
core/src/services/application_service.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
use entity::{candidate, parent};
|
||||||
|
use sea_orm::DbConn;
|
||||||
|
|
||||||
|
use crate::{error::ServiceError, candidate_details::{ApplicationDetails, EncryptedApplicationDetails}, Query, crypto};
|
||||||
|
|
||||||
|
use super::{parent_service::ParentService, candidate_service::CandidateService};
|
||||||
|
|
||||||
|
pub struct ApplicationService;
|
||||||
|
|
||||||
|
impl ApplicationService {
|
||||||
|
pub async fn create_candidate_with_parent( // uchazeč s maminkou 👩🍼
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
plain_text_password: &String,
|
||||||
|
personal_id_number: String,
|
||||||
|
) -> Result<(candidate::Model, parent::Model), ServiceError> {
|
||||||
|
Ok(
|
||||||
|
/* tokio::try_join!( // TODO: try_join! is not working
|
||||||
|
CandidateService::create(db, application_id, plain_text_password, personal_id_number),
|
||||||
|
ParentService::create(db, application_id)
|
||||||
|
)? */
|
||||||
|
|
||||||
|
|
||||||
|
(
|
||||||
|
CandidateService::create(db, application_id, plain_text_password, personal_id_number).await?,
|
||||||
|
ParentService::create(db, application_id).await?
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_all_details(
|
||||||
|
db: &DbConn,
|
||||||
|
application: i32,
|
||||||
|
form: ApplicationDetails,
|
||||||
|
) -> Result<(candidate::Model, parent::Model), ServiceError> {
|
||||||
|
let candidate = Query::find_candidate_by_id(db, application)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ServiceError::DbError)?
|
||||||
|
.ok_or(ServiceError::CandidateNotFound)?;
|
||||||
|
|
||||||
|
let parent = Query::find_parent_by_id(db, application)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ServiceError::DbError)?
|
||||||
|
.ok_or(ServiceError::ParentNotFound)?;
|
||||||
|
|
||||||
|
let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else {
|
||||||
|
return Err(ServiceError::DbError);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut admin_public_keys_refrence: Vec<&str> =
|
||||||
|
admin_public_keys.iter().map(|s| &**s).collect();
|
||||||
|
|
||||||
|
let mut recipients = vec![&*candidate.public_key];
|
||||||
|
recipients.append(&mut admin_public_keys_refrence);
|
||||||
|
|
||||||
|
let enc_details = EncryptedApplicationDetails::new(form, recipients).await?;
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
tokio::try_join!(
|
||||||
|
CandidateService::add_candidate_details(db, candidate, enc_details.clone()),
|
||||||
|
ParentService::add_parent_details(db, parent, enc_details.clone())
|
||||||
|
)?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn decrypt_all_details(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
password: String,
|
||||||
|
) -> Result<ApplicationDetails, ServiceError> {
|
||||||
|
let candidate = match Query::find_candidate_by_id(db, application_id).await {
|
||||||
|
Ok(candidate) => candidate.unwrap(),
|
||||||
|
Err(_) => return Err(ServiceError::DbError), // TODO: logging
|
||||||
|
};
|
||||||
|
let parent = Query::find_parent_by_id(db, application_id).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
match crypto::verify_password((&password).to_string(), candidate.code.clone()).await {
|
||||||
|
Ok(valid) => {
|
||||||
|
if !valid {
|
||||||
|
return Err(ServiceError::InvalidCredentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => return Err(ServiceError::InvalidCredentials),
|
||||||
|
}
|
||||||
|
|
||||||
|
let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
let enc_details = EncryptedApplicationDetails::try_from((candidate, parent))?;
|
||||||
|
|
||||||
|
enc_details.decrypt(dec_priv_key).await
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,188 +1,16 @@
|
||||||
use entity::candidate;
|
use entity::{candidate};
|
||||||
use sea_orm::{prelude::Uuid, DbConn};
|
use sea_orm::{prelude::Uuid, DbConn};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto::{self, hash_password},
|
crypto::{self, hash_password},
|
||||||
error::ServiceError,
|
error::ServiceError,
|
||||||
Mutation, Query,
|
Mutation, Query, candidate_details::{EncryptedApplicationDetails},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::session_service::{AdminUser, SessionService};
|
use super::{session_service::{AdminUser, SessionService}};
|
||||||
|
|
||||||
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
|
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
|
||||||
|
|
||||||
pub(crate) struct EncryptedUserDetails {
|
|
||||||
pub name: String,
|
|
||||||
pub surname: String,
|
|
||||||
pub birthplace: String,
|
|
||||||
// pub birthdate: NaiveDate,
|
|
||||||
pub address: String,
|
|
||||||
pub telephone: String,
|
|
||||||
pub citizenship: String,
|
|
||||||
pub email: String,
|
|
||||||
pub sex: String,
|
|
||||||
pub study: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EncryptedUserDetails {
|
|
||||||
pub async fn encrypt_form(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails {
|
|
||||||
let (
|
|
||||||
Ok(name),
|
|
||||||
Ok(surname),
|
|
||||||
Ok(birthplace),
|
|
||||||
// Ok(enc_birthdate),
|
|
||||||
Ok(address),
|
|
||||||
Ok(telephone),
|
|
||||||
Ok(citizenship),
|
|
||||||
Ok(email),
|
|
||||||
Ok(sex),
|
|
||||||
Ok(study),
|
|
||||||
) = tokio::join!(
|
|
||||||
crypto::encrypt_password_with_recipients(&form.name, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.surname, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.birthplace, &recipients),
|
|
||||||
// crypto::encrypt_password_with_recipients(&self.birthdate, &recipients), // TODO
|
|
||||||
crypto::encrypt_password_with_recipients(&form.address, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.telephone, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.citizenship, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.email, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.sex, &recipients),
|
|
||||||
crypto::encrypt_password_with_recipients(&form.study, &recipients),
|
|
||||||
) else {
|
|
||||||
panic!("Failed to encrypt user details"); // TODO
|
|
||||||
};
|
|
||||||
|
|
||||||
EncryptedUserDetails {
|
|
||||||
name,
|
|
||||||
surname,
|
|
||||||
birthplace,
|
|
||||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
|
||||||
address,
|
|
||||||
telephone,
|
|
||||||
citizenship,
|
|
||||||
email,
|
|
||||||
sex,
|
|
||||||
study,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_enc_candidate_details(
|
|
||||||
candidate: candidate::Model,
|
|
||||||
) -> Result<UserDetails, ServiceError> {
|
|
||||||
let ( // TODO: simplify??
|
|
||||||
Some(name),
|
|
||||||
Some(surname),
|
|
||||||
Some(birthplace),
|
|
||||||
// Some(birthdate),
|
|
||||||
Some(address),
|
|
||||||
Some(telephone),
|
|
||||||
Some(citizenship),
|
|
||||||
Some(email),
|
|
||||||
Some(sex),
|
|
||||||
Some(study),
|
|
||||||
) = (
|
|
||||||
candidate.name,
|
|
||||||
candidate.surname,
|
|
||||||
candidate.birthplace,
|
|
||||||
// candidate.birthdate,
|
|
||||||
candidate.address,
|
|
||||||
candidate.telephone,
|
|
||||||
candidate.citizenship,
|
|
||||||
candidate.email,
|
|
||||||
candidate.sex,
|
|
||||||
candidate.study,
|
|
||||||
) else {
|
|
||||||
return Err(ServiceError::CandidateDetailsNotSet);
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(UserDetails {
|
|
||||||
name,
|
|
||||||
surname,
|
|
||||||
birthplace,
|
|
||||||
// birthdate,
|
|
||||||
address,
|
|
||||||
telephone,
|
|
||||||
citizenship,
|
|
||||||
email,
|
|
||||||
sex,
|
|
||||||
study,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_model(candidate: candidate::Model) -> Result<EncryptedUserDetails, ServiceError> {
|
|
||||||
let Ok(details) = Self::extract_enc_candidate_details(candidate) else {
|
|
||||||
return Err(ServiceError::CandidateDetailsNotSet);
|
|
||||||
};
|
|
||||||
Ok(EncryptedUserDetails {
|
|
||||||
name: details.name,
|
|
||||||
surname: details.surname,
|
|
||||||
birthplace: details.birthplace,
|
|
||||||
// birthdate,
|
|
||||||
address: details.address,
|
|
||||||
telephone: details.telephone,
|
|
||||||
citizenship: details.citizenship,
|
|
||||||
email: details.email,
|
|
||||||
sex: details.sex,
|
|
||||||
study: details.study,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn decrypt(self, priv_key: String) -> Result<UserDetails, ServiceError> {
|
|
||||||
let (
|
|
||||||
Ok(name),
|
|
||||||
Ok(surname),
|
|
||||||
Ok(birthplace),
|
|
||||||
// Ok(enc_birthdate),
|
|
||||||
Ok(address),
|
|
||||||
Ok(telephone),
|
|
||||||
Ok(citizenship),
|
|
||||||
Ok(email),
|
|
||||||
Ok(sex),
|
|
||||||
Ok(study),
|
|
||||||
) = tokio::join!(
|
|
||||||
crypto::decrypt_password_with_private_key(&self.name, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.surname, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.birthplace, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.address, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.telephone, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.citizenship, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.email, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.sex, &priv_key),
|
|
||||||
crypto::decrypt_password_with_private_key(&self.study, &priv_key),
|
|
||||||
) else {
|
|
||||||
panic!("Failed to encrypt user details"); // TODO
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(UserDetails {
|
|
||||||
name,
|
|
||||||
surname,
|
|
||||||
birthplace,
|
|
||||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
|
||||||
address,
|
|
||||||
telephone,
|
|
||||||
citizenship,
|
|
||||||
email,
|
|
||||||
sex,
|
|
||||||
study,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct UserDetails {
|
|
||||||
pub name: String,
|
|
||||||
pub surname: String,
|
|
||||||
pub birthplace: String,
|
|
||||||
// pub birthdate: NaiveDate,
|
|
||||||
pub address: String,
|
|
||||||
pub telephone: String,
|
|
||||||
pub citizenship: String,
|
|
||||||
pub email: String,
|
|
||||||
pub sex: String,
|
|
||||||
pub study: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CandidateService;
|
pub struct CandidateService;
|
||||||
|
|
||||||
impl CandidateService {
|
impl CandidateService {
|
||||||
|
|
@ -191,7 +19,7 @@ impl CandidateService {
|
||||||
/// Hashed password
|
/// Hashed password
|
||||||
/// Encrypted private key
|
/// Encrypted private key
|
||||||
/// Public key
|
/// Public key
|
||||||
pub async fn create(
|
pub(in crate::services) async fn create(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
application_id: i32,
|
application_id: i32,
|
||||||
plain_text_password: &String,
|
plain_text_password: &String,
|
||||||
|
|
@ -224,9 +52,6 @@ impl CandidateService {
|
||||||
let Ok(hashed_personal_id_number) = hash_password(personal_id_number).await else {
|
let Ok(hashed_personal_id_number) = hash_password(personal_id_number).await else {
|
||||||
return Err(ServiceError::CryptoHashFailed);
|
return Err(ServiceError::CryptoHashFailed);
|
||||||
};
|
};
|
||||||
/* let encrypted_personal_id_number = crypto::encrypt_password_with_recipients(
|
|
||||||
&personal_id_number, &vec![&pubkey]
|
|
||||||
).await.unwrap(); */
|
|
||||||
|
|
||||||
Mutation::create_candidate(
|
Mutation::create_candidate(
|
||||||
db,
|
db,
|
||||||
|
|
@ -236,66 +61,25 @@ impl CandidateService {
|
||||||
pubkey,
|
pubkey,
|
||||||
encrypted_priv_key,
|
encrypted_priv_key,
|
||||||
)
|
)
|
||||||
.await
|
|
||||||
.map_err(|_| ServiceError::DbError)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn add_user_details(
|
|
||||||
db: &DbConn,
|
|
||||||
user: candidate::Model,
|
|
||||||
form: UserDetails,
|
|
||||||
) -> Result<entity::candidate::Model, ServiceError> {
|
|
||||||
let Ok(admin_public_keys) = Query::get_all_admin_public_keys(db).await else {
|
|
||||||
return Err(ServiceError::DbError);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut admin_public_keys_refrence: Vec<&str> =
|
|
||||||
admin_public_keys.iter().map(|s| &**s).collect();
|
|
||||||
|
|
||||||
let mut recipients = vec![&*user.public_key];
|
|
||||||
|
|
||||||
recipients.append(&mut admin_public_keys_refrence);
|
|
||||||
|
|
||||||
let enc_details = EncryptedUserDetails::encrypt_form(form, recipients).await;
|
|
||||||
|
|
||||||
Mutation::add_candidate_details(db, user, enc_details)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ServiceError::DbError)
|
.map_err(|_| ServiceError::DbError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn decrypt_details(
|
pub(in crate::services) async fn add_candidate_details(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
candidate_id: i32,
|
candidate: candidate::Model,
|
||||||
password: String,
|
enc_details: EncryptedApplicationDetails,
|
||||||
) -> Result<UserDetails, ServiceError> {
|
) -> Result<entity::candidate::Model, ServiceError> {
|
||||||
let candidate = match Query::find_candidate_by_id(db, candidate_id).await {
|
Mutation::add_candidate_details(db, candidate, enc_details.clone())
|
||||||
Ok(candidate) => candidate.unwrap(),
|
|
||||||
Err(_) => return Err(ServiceError::DbError), // TODO: logging
|
|
||||||
};
|
|
||||||
|
|
||||||
match crypto::verify_password((&password).to_string(), candidate.code.clone()).await {
|
|
||||||
Ok(valid) => {
|
|
||||||
if !valid {
|
|
||||||
return Err(ServiceError::InvalidCredentials);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => return Err(ServiceError::InvalidCredentials),
|
|
||||||
}
|
|
||||||
|
|
||||||
let dec_priv_key = crypto::decrypt_password(candidate.private_key.clone(), password)
|
|
||||||
.await
|
.await
|
||||||
.ok()
|
.map_err(|_| ServiceError::DbError)
|
||||||
.unwrap();
|
|
||||||
let enc_details = EncryptedUserDetails::from_model(candidate)?;
|
|
||||||
|
|
||||||
enc_details.decrypt(dec_priv_key).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_set_up(candidate: &candidate::Model) -> bool {
|
pub fn is_set_up(candidate: &candidate::Model) -> bool {
|
||||||
candidate.name.is_some() &&
|
candidate.name.is_some() &&
|
||||||
candidate.surname.is_some() &&
|
candidate.surname.is_some() &&
|
||||||
candidate.birthplace.is_some() &&
|
candidate.birthplace.is_some() &&
|
||||||
// birthdate: NaiveDate::from_ymd(2000, 1, 1),
|
candidate.birthdate.is_some() &&
|
||||||
candidate.address.is_some() &&
|
candidate.address.is_some() &&
|
||||||
candidate.telephone.is_some() &&
|
candidate.telephone.is_some() &&
|
||||||
candidate.citizenship.is_some() &&
|
candidate.citizenship.is_some() &&
|
||||||
|
|
@ -323,20 +107,9 @@ impl CandidateService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn decrypt_private_key(
|
async fn decrypt_private_key(
|
||||||
db: &DbConn,
|
candidate: candidate::Model,
|
||||||
candidate_id: i32,
|
|
||||||
password: String,
|
password: String,
|
||||||
) -> Result<String, ServiceError> {
|
) -> Result<String, ServiceError> {
|
||||||
let candidate = Query::find_candidate_by_id(db, candidate_id).await;
|
|
||||||
|
|
||||||
let Ok(candidate) = candidate else {
|
|
||||||
return Err(ServiceError::DbError);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(candidate) = candidate else {
|
|
||||||
return Err(ServiceError::UserNotFound);
|
|
||||||
};
|
|
||||||
|
|
||||||
let private_key_encrypted = candidate.private_key;
|
let private_key_encrypted = candidate.private_key;
|
||||||
|
|
||||||
let private_key = crypto::decrypt_password(private_key_encrypted, password).await;
|
let private_key = crypto::decrypt_password(private_key_encrypted, password).await;
|
||||||
|
|
@ -350,15 +123,20 @@ impl CandidateService {
|
||||||
|
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
user_id: i32,
|
candidate_id: i32,
|
||||||
password: String,
|
password: String,
|
||||||
ip_addr: String,
|
ip_addr: String,
|
||||||
) -> Result<(String, String), ServiceError> {
|
) -> Result<(String, String), ServiceError> {
|
||||||
|
let candidate = Query::find_candidate_by_id(db, candidate_id)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ServiceError::DbError)?
|
||||||
|
.ok_or(ServiceError::CandidateNotFound)?;
|
||||||
|
|
||||||
let session_id =
|
let session_id =
|
||||||
SessionService::new_session(db, Some(user_id), None, password.clone(), ip_addr).await;
|
SessionService::new_session(db, Some(candidate_id), None, password.clone(), ip_addr).await;
|
||||||
match session_id {
|
match session_id {
|
||||||
Ok(session_id) => {
|
Ok(session_id) => {
|
||||||
let private_key = Self::decrypt_private_key(db, user_id, password).await?;
|
let private_key = Self::decrypt_private_key(candidate, password).await?;
|
||||||
Ok((session_id, private_key))
|
Ok((session_id, private_key))
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
|
@ -368,7 +146,7 @@ impl CandidateService {
|
||||||
pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<candidate::Model, ServiceError> {
|
pub async fn auth(db: &DbConn, session_uuid: Uuid) -> Result<candidate::Model, ServiceError> {
|
||||||
match SessionService::auth_user_session(db, session_uuid).await {
|
match SessionService::auth_user_session(db, session_uuid).await {
|
||||||
Ok(user) => match user {
|
Ok(user) => match user {
|
||||||
AdminUser::User(candidate) => Ok(candidate),
|
AdminUser::Candidate(candidate) => Ok(candidate),
|
||||||
AdminUser::Admin(_) => Err(ServiceError::DbError),
|
AdminUser::Admin(_) => Err(ServiceError::DbError),
|
||||||
},
|
},
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
|
@ -388,15 +166,19 @@ impl CandidateService {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use entity::candidate::Model;
|
|
||||||
use sea_orm::{Database, DbConn};
|
use sea_orm::{Database, DbConn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto,
|
crypto,
|
||||||
services::candidate_service::{CandidateService, UserDetails},
|
services::candidate_service::{CandidateService}, Mutation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::EncryptedUserDetails;
|
use super::EncryptedApplicationDetails;
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use entity::{parent, candidate};
|
||||||
|
|
||||||
|
use crate::services::application_service::ApplicationService;
|
||||||
|
use crate::candidate_details::ApplicationDetails;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_application_id_validation() {
|
async fn test_application_id_validation() {
|
||||||
|
|
@ -411,7 +193,7 @@ mod tests {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
async fn get_memory_sqlite_connection() -> DbConn {
|
async fn get_memory_sqlite_connection() -> DbConn {
|
||||||
use entity::{admin, candidate};
|
use entity::{admin, candidate, parent};
|
||||||
use sea_orm::Schema;
|
use sea_orm::Schema;
|
||||||
use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend};
|
use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend};
|
||||||
|
|
||||||
|
|
@ -420,8 +202,8 @@ mod tests {
|
||||||
|
|
||||||
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(admin::Entity);
|
||||||
|
let stmt3: TableCreateStatement = schema.create_table_from_entity(parent::Entity);
|
||||||
|
|
||||||
db.execute(db.get_database_backend().build(&stmt))
|
db.execute(db.get_database_backend().build(&stmt))
|
||||||
.await
|
.await
|
||||||
|
|
@ -429,6 +211,9 @@ mod tests {
|
||||||
db.execute(db.get_database_backend().build(&stmt2))
|
db.execute(db.get_database_backend().build(&stmt2))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
db.execute(db.get_database_backend().build(&stmt3))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,11 +225,15 @@ mod tests {
|
||||||
|
|
||||||
let secret_message = "trnka".to_string();
|
let secret_message = "trnka".to_string();
|
||||||
|
|
||||||
|
|
||||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
|
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
Mutation::create_parent(&db, 103151)
|
||||||
|
.await.unwrap();
|
||||||
|
|
||||||
let encrypted_message =
|
let encrypted_message =
|
||||||
crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key])
|
crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key])
|
||||||
.await
|
.await
|
||||||
|
|
@ -464,53 +253,57 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
async fn put_user_data(db: &DbConn) -> Model {
|
async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) {
|
||||||
let plain_text_password = "test".to_string();
|
let plain_text_password = "test".to_string();
|
||||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
|
let (candidate, parent) = ApplicationService::create_candidate_with_parent(&db, 103151, &plain_text_password, "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let form = UserDetails {
|
let form = ApplicationDetails {
|
||||||
name: "test".to_string(),
|
name: "test".to_string(),
|
||||||
surname: "a".to_string(),
|
surname: "aaa".to_string(),
|
||||||
birthplace: "b".to_string(),
|
birthplace: "b".to_string(),
|
||||||
// birthdate: NaiveDate::from_ymd(1999, 1, 1),
|
birthdate: NaiveDate::from_ymd(1999, 1, 1),
|
||||||
address: "test".to_string(),
|
address: "test".to_string(),
|
||||||
telephone: "test".to_string(),
|
telephone: "test".to_string(),
|
||||||
citizenship: "test".to_string(),
|
citizenship: "test".to_string(),
|
||||||
email: "test".to_string(),
|
email: "test".to_string(),
|
||||||
sex: "test".to_string(),
|
sex: "test".to_string(),
|
||||||
study: "test".to_string(),
|
study: "test".to_string(),
|
||||||
|
parent_name: "test".to_string(),
|
||||||
|
parent_surname: "test".to_string(),
|
||||||
|
parent_telephone: "test".to_string(),
|
||||||
|
parent_email: "test".to_string(),
|
||||||
|
|
||||||
};
|
};
|
||||||
CandidateService::add_user_details(&db, candidate, form)
|
|
||||||
|
ApplicationService::add_all_details(&db, candidate.application, form)
|
||||||
.await
|
.await
|
||||||
.ok()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 = put_user_data(&db).await;
|
let (candidate, parent) = put_user_data(&db).await;
|
||||||
assert!(candidate.name.is_some());
|
assert!(candidate.name.is_some());
|
||||||
|
assert!(parent.name.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
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 = put_user_data(&db).await;
|
let (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(enc_candidate.private_key.clone(), password)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let dec_candidate = EncryptedUserDetails::from_model(enc_candidate)
|
let enc_details = EncryptedApplicationDetails::try_from((enc_candidate, enc_parent)).ok().unwrap();
|
||||||
.unwrap()
|
let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap();
|
||||||
.decrypt(dec_priv_key)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(dec_candidate.name, "test");
|
assert_eq!(dec_details.name, "test"); // TODO: test every element
|
||||||
|
assert_eq!(dec_details.parent_surname, "test");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
pub mod session_service;
|
pub mod session_service;
|
||||||
pub mod candidate_service;
|
pub mod candidate_service;
|
||||||
pub mod admin_service;
|
pub mod admin_service;
|
||||||
|
pub mod parent_service;
|
||||||
|
pub mod application_service;
|
||||||
31
core/src/services/parent_service.rs
Normal file
31
core/src/services/parent_service.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use entity::{parent};
|
||||||
|
use sea_orm::DbConn;
|
||||||
|
|
||||||
|
use crate::{error::ServiceError, Mutation, candidate_details::EncryptedApplicationDetails};
|
||||||
|
|
||||||
|
pub struct ParentService;
|
||||||
|
|
||||||
|
impl ParentService {
|
||||||
|
pub async fn create(
|
||||||
|
db: &DbConn,
|
||||||
|
application_id: i32,
|
||||||
|
) -> Result<parent::Model, ServiceError> {
|
||||||
|
let parent = Mutation::create_parent(db, application_id)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ServiceError::DbError)?;
|
||||||
|
|
||||||
|
Ok(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_parent_details(
|
||||||
|
db: &DbConn,
|
||||||
|
parent: parent::Model,
|
||||||
|
enc_details: EncryptedApplicationDetails,
|
||||||
|
) -> Result<parent::Model, ServiceError> {
|
||||||
|
let parent = Mutation::add_parent_details(db, parent, enc_details)
|
||||||
|
.await
|
||||||
|
.map_err(|_| ServiceError::DbError)?;
|
||||||
|
|
||||||
|
Ok(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
|
|
||||||
pub enum AdminUser {
|
pub enum AdminUser {
|
||||||
Admin(entity::admin::Model),
|
Admin(entity::admin::Model),
|
||||||
User(entity::candidate::Model),
|
Candidate(entity::candidate::Model),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::services) struct SessionService;
|
pub(in crate::services) struct SessionService;
|
||||||
|
|
@ -57,7 +57,7 @@ impl SessionService {
|
||||||
let candidate = match Query::find_candidate_by_id(db, user_id.unwrap()).await {
|
let candidate = match Query::find_candidate_by_id(db, user_id.unwrap()).await {
|
||||||
Ok(candidate) => match candidate {
|
Ok(candidate) => match candidate {
|
||||||
Some(candidate) => candidate,
|
Some(candidate) => candidate,
|
||||||
None => return Err(ServiceError::UserNotFound),
|
None => return Err(ServiceError::CandidateNotFound),
|
||||||
},
|
},
|
||||||
Err(_) => return Err(ServiceError::DbError),
|
Err(_) => return Err(ServiceError::DbError),
|
||||||
};
|
};
|
||||||
|
|
@ -78,7 +78,7 @@ impl SessionService {
|
||||||
let admin = match Query::find_admin_by_id(db, admin_id.unwrap()).await {
|
let admin = match Query::find_admin_by_id(db, admin_id.unwrap()).await {
|
||||||
Ok(admin) => match admin {
|
Ok(admin) => match admin {
|
||||||
Some(admin) => admin,
|
Some(admin) => admin,
|
||||||
None => return Err(ServiceError::UserNotFound),
|
None => return Err(ServiceError::CandidateNotFound),
|
||||||
},
|
},
|
||||||
Err(_) => return Err(ServiceError::DbError),
|
Err(_) => return Err(ServiceError::DbError),
|
||||||
};
|
};
|
||||||
|
|
@ -147,7 +147,7 @@ impl SessionService {
|
||||||
|
|
||||||
if candidate.is_ok() {
|
if candidate.is_ok() {
|
||||||
if let Some(candidate) = candidate.unwrap() {
|
if let Some(candidate) = candidate.unwrap() {
|
||||||
return Ok(AdminUser::User(candidate));
|
return Ok(AdminUser::Candidate(candidate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,7 +162,7 @@ impl SessionService {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use entity::{admin, candidate, session};
|
use entity::{admin, candidate, session, parent};
|
||||||
|
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
prelude::Uuid, sea_query::TableCreateStatement, ConnectionTrait, Database, DbBackend,
|
prelude::Uuid, sea_query::TableCreateStatement, ConnectionTrait, Database, DbBackend,
|
||||||
|
|
@ -171,7 +171,7 @@ mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
crypto,
|
crypto,
|
||||||
services::{candidate_service::CandidateService, session_service::SessionService},
|
services::{session_service::SessionService, application_service::ApplicationService},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -183,6 +183,7 @@ mod tests {
|
||||||
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(admin::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);
|
||||||
db.execute(db.get_database_backend().build(&stmt))
|
db.execute(db.get_database_backend().build(&stmt))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -192,6 +193,9 @@ mod tests {
|
||||||
db.execute(db.get_database_backend().build(&stmt3))
|
db.execute(db.get_database_backend().build(&stmt3))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
db.execute(db.get_database_backend().build(&stmt4))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,10 +205,10 @@ mod tests {
|
||||||
|
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let candidate = CandidateService::create(&db, 103151, &SECRET.to_string(), "".to_string())
|
let candidate = ApplicationService::create_candidate_with_parent(&db, 103151, &SECRET.to_string(), "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap();
|
.unwrap().0;
|
||||||
|
|
||||||
assert_eq!(candidate.application, 103151);
|
assert_eq!(candidate.application, 103151);
|
||||||
assert_ne!(candidate.code, SECRET.to_string());
|
assert_ne!(candidate.code, SECRET.to_string());
|
||||||
|
|
@ -218,10 +222,9 @@ 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;
|
||||||
|
|
||||||
CandidateService::create(&db, 103151, &"Tajny_kod".to_string(), "".to_string())
|
ApplicationService::create_candidate_with_parent(db, 103151, &"Tajny_kod".to_string(), "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.unwrap().0;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// correct password
|
// correct password
|
||||||
let session = SessionService::new_session(
|
let session = SessionService::new_session(
|
||||||
|
|
@ -231,9 +234,8 @@ mod tests {
|
||||||
"Tajny_kod".to_string(),
|
"Tajny_kod".to_string(),
|
||||||
"127.0.0.1".to_string(),
|
"127.0.0.1".to_string(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.ok()
|
.unwrap();
|
||||||
.unwrap();
|
|
||||||
// println!("{}", session.err().unwrap().1);
|
// println!("{}", session.err().unwrap().1);
|
||||||
assert!(
|
assert!(
|
||||||
SessionService::auth_user_session(db, Uuid::parse_str(&session).unwrap())
|
SessionService::auth_user_session(db, Uuid::parse_str(&session).unwrap())
|
||||||
|
|
@ -247,10 +249,9 @@ mod tests {
|
||||||
let db = &get_memory_sqlite_connection().await;
|
let db = &get_memory_sqlite_connection().await;
|
||||||
|
|
||||||
let candidate_form =
|
let candidate_form =
|
||||||
CandidateService::create(&db, 103151, &"Tajny_kod".to_string(), "".to_string())
|
ApplicationService::create_candidate_with_parent(&db, 103151, &"Tajny_kod".to_string(), "".to_string())
|
||||||
.await
|
.await
|
||||||
.ok()
|
.unwrap().0;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// incorrect password
|
// incorrect password
|
||||||
assert!(SessionService::new_session(
|
assert!(SessionService::new_session(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "admin")]
|
#[sea_orm(table_name = "admin")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(column_type = "Integer", primary_key)]
|
#[sea_orm(primary_key)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
use sea_orm::entity::prelude::*;
|
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(column_type = "Integer", primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub application: i32,
|
pub application: i32,
|
||||||
pub code: String,
|
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>,
|
||||||
pub birthplace: Option<String>,
|
pub birthplace: Option<String>,
|
||||||
pub birthdate: Option<Date>,
|
pub birthdate: Option<String>,
|
||||||
pub address: Option<String>,
|
pub address: Option<String>,
|
||||||
pub telephone: Option<String>,
|
pub telephone: Option<String>,
|
||||||
pub citizenship: Option<String>,
|
pub citizenship: Option<String>,
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub sex: Option<String>,
|
pub sex: Option<String>,
|
||||||
pub study: Option<String>,
|
pub study: Option<String>,
|
||||||
#[sea_orm(column_type = "Text", nullable)]
|
|
||||||
pub personal_identification_number: Option<String>,
|
pub personal_identification_number: Option<String>,
|
||||||
|
#[sea_orm(column_type = "Text")]
|
||||||
pub personal_identification_number_hash: String,
|
pub personal_identification_number_hash: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
pub private_key: String,
|
pub private_key: String,
|
||||||
|
|
@ -28,7 +30,7 @@ pub struct Model {
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
#[sea_orm(has_one = "super::parent::Entity")]
|
#[sea_orm(has_many = "super::parent::Entity")]
|
||||||
Parent,
|
Parent,
|
||||||
#[sea_orm(has_many = "super::session::Entity")]
|
#[sea_orm(has_many = "super::session::Entity")]
|
||||||
Session,
|
Session,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
|
@ -18,7 +20,9 @@ pub enum Relation {
|
||||||
#[sea_orm(
|
#[sea_orm(
|
||||||
belongs_to = "super::candidate::Entity",
|
belongs_to = "super::candidate::Entity",
|
||||||
from = "Column::Application",
|
from = "Column::Application",
|
||||||
to = "super::candidate::Column::Application"
|
to = "super::candidate::Column::Application",
|
||||||
|
on_update = "Cascade",
|
||||||
|
on_delete = "Cascade"
|
||||||
)]
|
)]
|
||||||
Candidate,
|
Candidate,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
pub use super::admin::Entity as Admin;
|
pub use super::admin::Entity as Admin;
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3
|
||||||
|
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||||
|
|
@ -5,10 +7,8 @@ use sea_orm::entity::prelude::*;
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
#[sea_orm(column_type = "Integer", nullable)]
|
|
||||||
pub admin_id: Option<i32>,
|
|
||||||
#[sea_orm(column_type = "Integer", nullable)]
|
|
||||||
pub user_id: Option<i32>,
|
pub user_id: Option<i32>,
|
||||||
|
pub admin_id: Option<i32>,
|
||||||
pub ip_address: String,
|
pub ip_address: String,
|
||||||
pub created_at: DateTime,
|
pub created_at: DateTime,
|
||||||
pub expires_at: DateTime,
|
pub expires_at: DateTime,
|
||||||
|
|
@ -34,16 +34,16 @@ pub enum Relation {
|
||||||
Candidate,
|
Candidate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Related<super::candidate::Entity> for Entity {
|
|
||||||
fn to() -> RelationDef {
|
|
||||||
Relation::Candidate.def()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Related<super::admin::Entity> for Entity {
|
impl Related<super::admin::Entity> for Entity {
|
||||||
fn to() -> RelationDef {
|
fn to() -> RelationDef {
|
||||||
Relation::Admin.def()
|
Relation::Admin.def()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Related<super::candidate::Entity> for Entity {
|
||||||
|
fn to() -> RelationDef {
|
||||||
|
Relation::Candidate.def()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ mod m20221024_134454_insert_sample_admin;
|
||||||
mod m20221025_154422_create_session;
|
mod m20221025_154422_create_session;
|
||||||
mod m20221027_194728_session_create_user_fk;
|
mod m20221027_194728_session_create_user_fk;
|
||||||
mod m20221028_194728_session_create_admin_fk;
|
mod m20221028_194728_session_create_admin_fk;
|
||||||
mod m20221030_133428_parent_create_candidate_fk;
|
mod m20221112_112212_create_parent_candidate_fk;
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
@ -21,7 +21,7 @@ impl MigratorTrait for Migrator {
|
||||||
Box::new(m20221025_154422_create_session::Migration),
|
Box::new(m20221025_154422_create_session::Migration),
|
||||||
Box::new(m20221027_194728_session_create_user_fk::Migration),
|
Box::new(m20221027_194728_session_create_user_fk::Migration),
|
||||||
Box::new(m20221028_194728_session_create_admin_fk::Migration),
|
Box::new(m20221028_194728_session_create_admin_fk::Migration),
|
||||||
Box::new(m20221030_133428_parent_create_candidate_fk::Migration),
|
Box::new(m20221112_112212_create_parent_candidate_fk::Migration),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ impl MigrationTrait for Migration {
|
||||||
.col(ColumnDef::new(Candidate::Surname).string())
|
.col(ColumnDef::new(Candidate::Surname).string())
|
||||||
.col(ColumnDef::new(Candidate::BirthSurname).string())
|
.col(ColumnDef::new(Candidate::BirthSurname).string())
|
||||||
.col(ColumnDef::new(Candidate::Birthplace).string())
|
.col(ColumnDef::new(Candidate::Birthplace).string())
|
||||||
.col(ColumnDef::new(Candidate::Birthdate).date())
|
.col(ColumnDef::new(Candidate::Birthdate).string())
|
||||||
.col(ColumnDef::new(Candidate::Address).string())
|
.col(ColumnDef::new(Candidate::Address).string())
|
||||||
.col(ColumnDef::new(Candidate::Telephone).string())
|
.col(ColumnDef::new(Candidate::Telephone).string())
|
||||||
.col(ColumnDef::new(Candidate::Citizenship).string())
|
.col(ColumnDef::new(Candidate::Citizenship).string())
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue