mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-17 21:41:20 +00:00
feat: link candidates
This commit is contained in:
parent
61facc503c
commit
6e1c35f721
9 changed files with 110 additions and 19 deletions
|
|
@ -85,15 +85,16 @@ pub async fn hello(_session: AdminAuth) -> Result<String, Custom<String>> {
|
|||
#[post("/create", data = "<request>")]
|
||||
pub async fn create_candidate(
|
||||
conn: Connection<'_, Db>,
|
||||
_session: AdminAuth,
|
||||
session: AdminAuth,
|
||||
request: Json<RegisterRequest>,
|
||||
) -> Result<Json<CreateCandidateResponse>, Custom<String>> {
|
||||
let db = conn.into_inner();
|
||||
let form = request.into_inner();
|
||||
let private_key = session.get_private_key();
|
||||
|
||||
let plain_text_password = random_12_char_string();
|
||||
|
||||
let application = ApplicationService::create(&db, form.application_id, &plain_text_password, form.personal_id_number.clone())
|
||||
let application = ApplicationService::create(&private_key, &db, form.application_id, &plain_text_password, form.personal_id_number.clone())
|
||||
.await
|
||||
.map_err(to_custom_error)?;
|
||||
|
||||
|
|
@ -161,10 +162,12 @@ pub async fn get_candidate(
|
|||
let db = conn.into_inner();
|
||||
let private_key = session.get_private_key();
|
||||
|
||||
let candidate = Query::find_candidate_by_id(db, id)
|
||||
let application = Query::find_application_by_id(db, id)
|
||||
.await
|
||||
.map_err(|e| to_custom_error(ServiceError::DbError(e)))?
|
||||
.ok_or(to_custom_error(ServiceError::CandidateNotFound))?;
|
||||
let candidate = ApplicationService::find_related_candidate(db, application).await
|
||||
.map_err(to_custom_error)?;
|
||||
|
||||
let details = ApplicationService::decrypt_all_details(
|
||||
private_key,
|
||||
|
|
@ -187,10 +190,13 @@ pub async fn delete_candidate(
|
|||
) -> Result<(), Custom<String>> {
|
||||
let db = conn.into_inner();
|
||||
|
||||
let candidate = Query::find_candidate_by_id(db, id)
|
||||
let application = Query::find_application_by_id(db, id)
|
||||
.await
|
||||
.map_err(|e| to_custom_error(ServiceError::DbError(e)))?
|
||||
.ok_or(to_custom_error(ServiceError::CandidateNotFound))?;
|
||||
let candidate = ApplicationService::find_related_candidate(db, application).await.map_err(to_custom_error)?;
|
||||
|
||||
// TODO
|
||||
|
||||
CandidateService::delete_candidate(db, candidate)
|
||||
.await
|
||||
|
|
@ -219,12 +225,19 @@ pub async fn reset_candidate_password(
|
|||
|
||||
#[get("/candidate/<id>/portfolio")]
|
||||
pub async fn get_candidate_portfolio(
|
||||
conn: Connection<'_, Db>,
|
||||
session: AdminAuth,
|
||||
id: i32,
|
||||
) -> Result<Vec<u8>, Custom<String>> {
|
||||
let db = conn.into_inner();
|
||||
let private_key = session.get_private_key();
|
||||
|
||||
let portfolio = PortfolioService::get_portfolio(id, private_key)
|
||||
let application = Query::find_application_by_id(db, id)
|
||||
.await
|
||||
.map_err(|e| to_custom_error(ServiceError::DbError(e)))?
|
||||
.ok_or(to_custom_error(ServiceError::CandidateNotFound))?;
|
||||
|
||||
let portfolio = PortfolioService::get_portfolio(application.candidate_id, private_key)
|
||||
.await
|
||||
.map_err(to_custom_error)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ pub mod tests {
|
|||
.unwrap();
|
||||
|
||||
let application = ApplicationService::create(
|
||||
&"".to_string(),
|
||||
db,
|
||||
APPLICATION_ID,
|
||||
&CANDIDATE_PASSWORD.to_string(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use ::entity::application;
|
||||
use log::{info, warn};
|
||||
use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||
use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, EntityTrait, IntoActiveModel, QueryFilter, ColumnTrait};
|
||||
|
||||
use crate::Mutation;
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,20 @@ impl Mutation {
|
|||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn update_personal_id(
|
||||
db: &DbConn,
|
||||
candidate: candidate::Model,
|
||||
personal_id: &str,
|
||||
) -> Result<candidate::Model, DbErr> {
|
||||
let mut candidate = candidate.into_active_model();
|
||||
candidate.personal_identification_number = Set(personal_id.to_string());
|
||||
|
||||
candidate
|
||||
.update(db)
|
||||
.await
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use entity::{application, candidate};
|
||||
use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait, FromQueryResult, QuerySelect, JoinType, RelationTrait};
|
||||
use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait, FromQueryResult, QuerySelect, JoinType, RelationTrait, QueryFilter, ColumnTrait};
|
||||
|
||||
#[derive(FromQueryResult, Clone)]
|
||||
pub struct ApplicationCandidateJoin {
|
||||
|
|
@ -52,4 +52,16 @@ impl Query {
|
|||
.all(db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn find_applications_by_candidate_id(
|
||||
db: &DbConn,
|
||||
candidate_id: i32,
|
||||
) -> Result<Vec<application::Model>, DbErr> {
|
||||
let applications = application::Entity::find()
|
||||
.filter(application::Column::CandidateId.eq(candidate_id))
|
||||
.all(db)
|
||||
.await?;
|
||||
|
||||
Ok(applications)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,12 @@ use crate::Query;
|
|||
|
||||
pub const PAGE_SIZE: u64 = 20;
|
||||
|
||||
#[derive(FromQueryResult)]
|
||||
pub struct IdPersonalIdNumberJoin {
|
||||
pub id: i32,
|
||||
pub personal_id_number: String,
|
||||
}
|
||||
|
||||
#[derive(FromQueryResult)]
|
||||
pub struct ApplicationId {
|
||||
application: i32,
|
||||
|
|
@ -81,7 +87,16 @@ impl Query {
|
|||
.all(db)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
pub async fn find_candidate_by_personal_id(
|
||||
db: &DbConn,
|
||||
personal_id: &str,
|
||||
) -> Result<Option<candidate::Model>, DbErr> {
|
||||
Candidate::find()
|
||||
.filter(candidate::Column::PersonalIdentificationNumber.eq(personal_id))
|
||||
.one(db)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ impl ApplicationService {
|
|||
/// Encrypted private key
|
||||
/// Public key
|
||||
pub async fn create(
|
||||
admin_private_key: &String,
|
||||
db: &DbConn,
|
||||
application_id: i32,
|
||||
plain_text_password: &String,
|
||||
|
|
@ -35,7 +36,6 @@ impl ApplicationService {
|
|||
{
|
||||
return Err(ServiceError::UserAlreadyExists);
|
||||
}
|
||||
|
||||
|
||||
let hashed_password = hash_password(plain_text_password.to_string()).await?;
|
||||
let (pubkey, priv_key_plain_text) = crypto::create_identity();
|
||||
|
|
@ -43,20 +43,20 @@ impl ApplicationService {
|
|||
priv_key_plain_text,
|
||||
plain_text_password.to_string()
|
||||
).await?;
|
||||
|
||||
|
||||
let recipients = get_recipients(db, &pubkey).await?;
|
||||
let enc_personal_id_number = EncryptedString::new(
|
||||
&personal_id_number,
|
||||
&recipients,
|
||||
).await?;
|
||||
|
||||
let candidate = CandidateService::create(
|
||||
|
||||
let candidate = Self::find_or_create_candidate_with_personal_id(
|
||||
admin_private_key,
|
||||
db,
|
||||
enc_personal_id_number.clone().to_string()
|
||||
personal_id_number,
|
||||
&enc_personal_id_number,
|
||||
).await?;
|
||||
|
||||
println!("candidate: {:?}", candidate);
|
||||
|
||||
let application = Mutation::create_application(
|
||||
db,
|
||||
application_id,
|
||||
|
|
@ -70,6 +70,41 @@ impl ApplicationService {
|
|||
Ok(application)
|
||||
}
|
||||
|
||||
async fn find_or_create_candidate_with_personal_id(
|
||||
admin_private_key: &String,
|
||||
db: &DbConn,
|
||||
personal_id_number: String,
|
||||
enc_personal_id_number: &EncryptedString,
|
||||
) -> Result<candidate::Model, ServiceError> {
|
||||
let candidates = Query::list_candidates_full(db).await?;
|
||||
let ids_decrypted = futures::future::join_all(
|
||||
candidates.iter().map(|c| async {(
|
||||
c.id,
|
||||
EncryptedString::from(c.personal_identification_number.clone())
|
||||
.decrypt(admin_private_key)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
)}
|
||||
))
|
||||
.await;
|
||||
|
||||
let found_ids: Vec<&(i32, String)> = ids_decrypted
|
||||
.iter()
|
||||
.filter(|(_, id)| id == &personal_id_number)
|
||||
.collect();
|
||||
// TODO: take the candidate id directly from the iterator
|
||||
if found_ids.iter().any(|(_, personal_id)| personal_id == &personal_id_number) {
|
||||
let candidate = Query::find_candidate_by_id(db, found_ids[0].0)
|
||||
.await?
|
||||
.ok_or(ServiceError::CandidateNotFound)?;
|
||||
let candidate = Mutation::update_personal_id(db, candidate, &enc_personal_id_number.to_owned().to_string()).await?;
|
||||
println!("Candidates linked!");
|
||||
Ok(candidate)
|
||||
} else {
|
||||
CandidateService::create(db, enc_personal_id_number.to_owned().to_string()).await
|
||||
}
|
||||
}
|
||||
|
||||
fn is_application_id_valid(application_id: i32) -> bool {
|
||||
let s = &application_id.to_string();
|
||||
if s.len() <= 3 {
|
||||
|
|
@ -368,7 +403,7 @@ mod tests {
|
|||
|
||||
let secret_message = "trnka".to_string();
|
||||
|
||||
let application = ApplicationService::create(&db, 103100, &plain_text_password, "".to_string()).await.unwrap();
|
||||
let application = ApplicationService::create(&"".to_string(), &db, 103100, &plain_text_password, "".to_string()).await.unwrap();
|
||||
|
||||
let encrypted_message =
|
||||
crypto::encrypt_password_with_recipients(&secret_message, &vec![&application.public_key])
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ pub mod tests {
|
|||
|
||||
let plain_text_password = "test".to_string();
|
||||
let application = ApplicationService::create(
|
||||
&"".to_string(),
|
||||
db,
|
||||
APPLICATION_ID,
|
||||
&plain_text_password,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ mod tests {
|
|||
|
||||
let db = get_memory_sqlite_connection().await;
|
||||
|
||||
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
|
||||
assert_eq!(application.id.to_owned(), 103151);
|
||||
assert_ne!(application.password.to_owned(), SECRET.to_string());
|
||||
|
|
@ -66,7 +66,7 @@ mod tests {
|
|||
async fn test_candidate_session_correct_password() {
|
||||
let db = &get_memory_sqlite_connection().await;
|
||||
|
||||
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
|
||||
// correct password
|
||||
let session = ApplicationService::new_session(
|
||||
|
|
@ -89,7 +89,7 @@ mod tests {
|
|||
async fn test_candidate_session_incorrect_password() {
|
||||
let db = &get_memory_sqlite_connection().await;
|
||||
|
||||
let application = ApplicationService::create(&db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
let application = ApplicationService::create(&"".to_string(), &db, 103151, &SECRET.to_string(), "".to_string()).await.unwrap();
|
||||
|
||||
// incorrect password
|
||||
assert!(ApplicationService::new_session(
|
||||
|
|
|
|||
Loading…
Reference in a new issue