mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-17 21:41:20 +00:00
feat: remove structs in favor of function arguments, tokio::join for async encryption
This commit is contained in:
parent
2833d9850b
commit
0a33695210
1 changed files with 137 additions and 101 deletions
|
|
@ -1,58 +1,17 @@
|
|||
use chrono::NaiveDate;
|
||||
use entity::candidate;
|
||||
use sea_orm::{DbConn, prelude::Uuid};
|
||||
use sea_orm::{prelude::Uuid, DbConn};
|
||||
|
||||
use crate::{Mutation, crypto::{hash_password, self}, error::{ServiceError}, Query};
|
||||
use crate::{
|
||||
crypto::{self, hash_password},
|
||||
error::ServiceError,
|
||||
Mutation, Query,
|
||||
};
|
||||
|
||||
use super::session_service::SessionService;
|
||||
|
||||
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
|
||||
|
||||
pub struct EncryptedAddUserData {
|
||||
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 AddUserDetailsForm {
|
||||
pub application_id: i32,
|
||||
|
||||
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 AddUserDetailsForm {
|
||||
pub async fn to_encrypted(self, recipients: Vec<&str>) -> EncryptedAddUserData {
|
||||
EncryptedAddUserData {
|
||||
name: crypto::encrypt_password_with_recipients(&self.name, &recipients).await.unwrap(),
|
||||
surname: crypto::encrypt_password_with_recipients(&self.surname, &recipients).await.unwrap(),
|
||||
birthplace: crypto::encrypt_password_with_recipients(&self.birthplace, &recipients).await.unwrap(),
|
||||
birthdate: self.birthdate, // TODO: encrypt
|
||||
address: crypto::encrypt_password_with_recipients(&self.address, &recipients).await.unwrap(),
|
||||
telephone: crypto::encrypt_password_with_recipients(&self.telephone, &recipients).await.unwrap(),
|
||||
citizenship: crypto::encrypt_password_with_recipients(&self.citizenship, &recipients).await.unwrap(),
|
||||
email: crypto::encrypt_password_with_recipients(&self.email, &recipients).await.unwrap(),
|
||||
sex: crypto::encrypt_password_with_recipients(&self.sex, &recipients).await.unwrap(),
|
||||
study: crypto::encrypt_password_with_recipients(&self.study, &recipients).await.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CandidateService;
|
||||
|
||||
impl CandidateService {
|
||||
|
|
@ -65,68 +24,132 @@ impl CandidateService {
|
|||
db: &DbConn,
|
||||
application_id: i32,
|
||||
plain_text_password: &String,
|
||||
personal_id_number: String
|
||||
) -> Result<candidate::Model, ServiceError>{
|
||||
personal_id_number: String,
|
||||
) -> Result<candidate::Model, ServiceError> {
|
||||
// Check if application id starts with 101, 102 or 103
|
||||
if !CandidateService::is_application_id_valid(application_id) {
|
||||
return Err(ServiceError::InvalidApplicationId)
|
||||
return Err(ServiceError::InvalidApplicationId);
|
||||
}
|
||||
|
||||
// Check if user with that application id already exists
|
||||
if Query::find_candidate_by_id(db, application_id).await.unwrap().is_some() {
|
||||
return Err(ServiceError::UserAlreadyExists)
|
||||
if Query::find_candidate_by_id(db, application_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_some()
|
||||
{
|
||||
return Err(ServiceError::UserAlreadyExists);
|
||||
}
|
||||
|
||||
// TODO: unwrap pro testing..
|
||||
let hashed_password = hash_password(plain_text_password.to_string()).await.unwrap();
|
||||
let hashed_password = hash_password(plain_text_password.to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
||||
let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text, plain_text_password.to_string()).await.unwrap();
|
||||
let encrypted_priv_key =
|
||||
crypto::encrypt_password(priv_key_plain_text, plain_text_password.to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let encrypted_personal_id_number = crypto::encrypt_password_with_recipients(
|
||||
let hashed_personal_id_number = hash_password(personal_id_number).await.unwrap();
|
||||
/* let encrypted_personal_id_number = crypto::encrypt_password_with_recipients(
|
||||
&personal_id_number, &vec![&pubkey]
|
||||
).await.unwrap();
|
||||
).await.unwrap(); */
|
||||
|
||||
Mutation::create_candidate(
|
||||
db,
|
||||
application_id,
|
||||
hashed_password,
|
||||
encrypted_personal_id_number,
|
||||
hashed_personal_id_number,
|
||||
pubkey,
|
||||
encrypted_priv_key
|
||||
encrypted_priv_key,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| ServiceError::DbError)
|
||||
.await
|
||||
.map_err(|_| ServiceError::DbError)
|
||||
}
|
||||
|
||||
pub async fn add_user_details(
|
||||
db: &DbConn,
|
||||
details: AddUserDetailsForm,
|
||||
application_id: i32,
|
||||
name: String,
|
||||
surname: String,
|
||||
birthplace: String,
|
||||
birthdate: String,
|
||||
address: String,
|
||||
telephone: String,
|
||||
citizenship: String,
|
||||
email: String,
|
||||
sex: String,
|
||||
study: String,
|
||||
) -> Result<entity::candidate::Model, sea_orm::DbErr> {
|
||||
let user = Query::find_candidate_by_id(db, details.application_id).await.unwrap().unwrap();
|
||||
let recipients = vec![&*user.public_key];
|
||||
let encrypted = details.to_encrypted(recipients).await;
|
||||
Mutation::add_user_details(db, user, encrypted).await
|
||||
let user = Query::find_candidate_by_id(db, application_id)
|
||||
.await?
|
||||
.unwrap();
|
||||
|
||||
let admin_public_keys = Query::get_all_admin_public_keys(db).await?;
|
||||
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_name,
|
||||
enc_surname,
|
||||
enc_birthplace,
|
||||
enc_birthdate,
|
||||
enc_address,
|
||||
enc_telephone,
|
||||
enc_citizenship,
|
||||
enc_email,
|
||||
enc_sex,
|
||||
enc_study,
|
||||
) = tokio::join!(
|
||||
crypto::encrypt_password_with_recipients(&name, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&surname, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&birthplace, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&birthdate, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&address, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&telephone, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&citizenship, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&email, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&sex, &recipients),
|
||||
crypto::encrypt_password_with_recipients(&study, &recipients),
|
||||
);
|
||||
|
||||
Mutation::add_candidate_details(
|
||||
db,
|
||||
user,
|
||||
enc_name.unwrap(),
|
||||
enc_surname.unwrap(),
|
||||
enc_birthplace.unwrap(),
|
||||
enc_birthdate.unwrap(),
|
||||
enc_address.unwrap(),
|
||||
enc_telephone.unwrap(),
|
||||
enc_citizenship.unwrap(),
|
||||
enc_email.unwrap(),
|
||||
enc_sex.unwrap(),
|
||||
enc_study.unwrap(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn login(
|
||||
db: &DbConn,
|
||||
user_id: i32,
|
||||
password: String,
|
||||
ip_addr: String
|
||||
ip_addr: String,
|
||||
) -> Result<String, ServiceError> {
|
||||
SessionService::new_session(db, user_id, password, ip_addr).await
|
||||
}
|
||||
|
||||
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> {
|
||||
SessionService::auth_user_session(db, session_uuid).await
|
||||
}
|
||||
|
||||
fn is_application_id_valid(application_id: i32) -> bool {
|
||||
let s = &application_id.to_string();
|
||||
if s.len() <= 3 { // TODO: does the field of study prefix have to be exactly 6 digits?
|
||||
if s.len() <= 3 {
|
||||
// TODO: does the field of study prefix have to be exactly 6 digits?
|
||||
return false;
|
||||
}
|
||||
let field_of_study_prefix = &s[0..3];
|
||||
|
|
@ -134,7 +157,6 @@ impl CandidateService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::NaiveDate;
|
||||
|
|
@ -142,8 +164,6 @@ mod tests {
|
|||
|
||||
use crate::{crypto, services::candidate_service::CandidateService};
|
||||
|
||||
use super::AddUserDetailsForm;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_application_id_validation() {
|
||||
assert!(CandidateService::is_application_id_valid(101_101));
|
||||
|
|
@ -158,16 +178,17 @@ mod tests {
|
|||
#[cfg(test)]
|
||||
async fn get_memory_sqlite_connection() -> DbConn {
|
||||
use entity::candidate;
|
||||
use sea_orm::{DbBackend, sea_query::TableCreateStatement, ConnectionTrait};
|
||||
use sea_orm::Schema;
|
||||
|
||||
use sea_orm::{sea_query::TableCreateStatement, ConnectionTrait, DbBackend};
|
||||
|
||||
let base_url = "sqlite::memory:";
|
||||
let db: DbConn = Database::connect(base_url).await.unwrap();
|
||||
|
||||
|
||||
let schema = Schema::new(DbBackend::Sqlite);
|
||||
let stmt: TableCreateStatement = schema.create_table_from_entity(candidate::Entity);
|
||||
db.execute(db.get_database_backend().build(&stmt)).await.unwrap();
|
||||
db.execute(db.get_database_backend().build(&stmt))
|
||||
.await
|
||||
.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
|
|
@ -179,41 +200,56 @@ mod tests {
|
|||
|
||||
let secret_message = "trnka".to_string();
|
||||
|
||||
|
||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()).await.ok().unwrap();
|
||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let encrypted_message = crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key]).await.unwrap();
|
||||
let encrypted_message =
|
||||
crypto::encrypt_password_with_recipients(&secret_message, &vec![&candidate.public_key])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let private_key_plain_text = crypto::decrypt_password(candidate.private_key, plain_text_password).await.unwrap();
|
||||
let private_key_plain_text =
|
||||
crypto::decrypt_password(candidate.private_key, plain_text_password)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let decrypted_message = crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text).await.unwrap();
|
||||
let decrypted_message =
|
||||
crypto::decrypt_password_with_private_key(&encrypted_message, &private_key_plain_text)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(secret_message, decrypted_message);
|
||||
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_user_data() {
|
||||
let db = get_memory_sqlite_connection().await;
|
||||
let plain_text_password = "test".to_string();
|
||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string()).await.ok().unwrap();
|
||||
let candidate = CandidateService::create(&db, 103151, &plain_text_password, "".to_string())
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let form = AddUserDetailsForm {
|
||||
application_id: candidate.application,
|
||||
name: "test".to_string(),
|
||||
surname: "a".to_string(),
|
||||
birthplace: "b".to_string(),
|
||||
birthdate: NaiveDate::from_ymd(1999, 1, 1),
|
||||
address: "test".to_string(),
|
||||
telephone: "test".to_string(),
|
||||
citizenship: "test".to_string(),
|
||||
email: "test".to_string(),
|
||||
sex: "test".to_string(),
|
||||
study: "test".to_string(),
|
||||
};
|
||||
|
||||
let candidate = CandidateService::add_user_details(&db, form).await.ok().unwrap();
|
||||
let candidate = CandidateService::add_user_details(
|
||||
&db,
|
||||
candidate.application,
|
||||
"test".to_string(),
|
||||
"a".to_string(),
|
||||
"b".to_string(),
|
||||
NaiveDate::from_ymd(1999, 1, 1).to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
"test".to_string(),
|
||||
)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
assert!(candidate.name.is_some());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue