Merge pull request #40 from EETagent/parent_details

Parent details, massive refactoring
This commit is contained in:
Sebastian Pravda 2022-11-12 11:50:38 +01:00 committed by GitHub
commit 72ae61637f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 498 additions and 348 deletions

View file

@ -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)
), ),
} }

View file

@ -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)
} }

View file

@ -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()));

View 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,
}

View file

@ -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());

View file

@ -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());

View file

@ -3,3 +3,4 @@ pub struct Query;
pub mod candidate; pub mod candidate;
pub mod admin; pub mod admin;
pub mod session; pub mod session;
pub mod parent;

View 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
}
}

View file

@ -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()),

View file

@ -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::*;

View file

@ -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),
} }

View 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
}
}

View file

@ -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");
} }
} }

View file

@ -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;

View 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)
}
}

View file

@ -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(

View file

@ -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,

View file

@ -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,

View file

@ -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;

View file

@ -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,
} }

View file

@ -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;

View file

@ -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 {}

View file

@ -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),
] ]
} }
} }

View file

@ -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())