feat: link candidates

This commit is contained in:
Sebastian Pravda 2023-01-14 16:27:07 +01:00
parent 61facc503c
commit 6e1c35f721
No known key found for this signature in database
GPG key ID: F3BC84F08EFA3F57
9 changed files with 110 additions and 19 deletions

View file

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

View file

@ -43,6 +43,7 @@ pub mod tests {
.unwrap();
let application = ApplicationService::create(
&"".to_string(),
db,
APPLICATION_ID,
&CANDIDATE_PASSWORD.to_string(),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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