Merge pull request #125 from EETagent/password_reset_fix

Fix Password reset, improve EncryptedApplication data structure
This commit is contained in:
Vojtěch Jungmann 2023-01-08 20:39:19 +01:00 committed by GitHub
commit 9a9d4355a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 193 additions and 203 deletions

View file

@ -108,7 +108,6 @@ pub async fn get_details(
let private_key = session.get_private_key();
let candidate: entity::candidate::Model = session.into();
// let handle = tokio::spawn(async move {
let details = ApplicationService::decrypt_all_details(private_key, db, candidate)
.await
.map(|x| Json(x))

View file

@ -26,7 +26,7 @@ impl Mutation {
.insert(db)
.await?;
info!("CANDIDATE CREATED");
info!("CANDIDATE {} CREATED", application_id);
Ok(insert)
}
@ -48,6 +48,7 @@ impl Mutation {
pub_key: String,
priv_key_enc: String,
) -> Result<candidate::Model, DbErr> {
let application = candidate.application;
let mut candidate: candidate::ActiveModel = candidate.into();
candidate.code = Set(new_password_hash);
candidate.public_key = Set(pub_key);
@ -55,33 +56,34 @@ impl Mutation {
let update = candidate.update(db).await?;
warn!("CANDIDATE PASSWORD CHANGED");
warn!("CANDIDATE {} PASSWORD CHANGED", application);
Ok(update)
}
pub async fn add_candidate_details(
pub async fn update_candidate_details(
db: &DbConn,
user: candidate::Model,
enc_candidate: EncryptedCandidateDetails,
) -> Result<candidate::Model, sea_orm::DbErr> {
let application = user.application;
let mut user: candidate::ActiveModel = user.into();
user.name = Set(Some(enc_candidate.name.into()));
user.surname = Set(Some(enc_candidate.surname.into()));
user.birthplace = Set(Some(enc_candidate.birthplace.into()));
user.birthdate = Set(Some(enc_candidate.birthdate.into()));
user.address = Set(Some(enc_candidate.address.into()));
user.telephone = Set(Some(enc_candidate.telephone.into()));
user.citizenship = Set(Some(enc_candidate.citizenship.into()));
user.email = Set(Some(enc_candidate.email.into()));
user.sex = Set(Some(enc_candidate.sex.into()));
user.personal_identification_number = Set(enc_candidate.personal_id_number.into()); // TODO: do not set this here, it is already set in the create_candidate mutation???
user.study = Set(Some(enc_candidate.study.into()));
user.name = Set(enc_candidate.name.map(|e| e.into()));
user.surname = Set(enc_candidate.surname.map(|e| e.into()));
user.birthplace = Set(enc_candidate.birthplace.map(|e| e.into()));
user.birthdate = Set(enc_candidate.birthdate.map(|e| e.into()));
user.address = Set(enc_candidate.address.map(|e| e.into()));
user.telephone = Set(enc_candidate.telephone.map(|e| e.into()));
user.citizenship = Set(enc_candidate.citizenship.map(|e| e.into()));
user.email = Set(enc_candidate.email.map(|e| e.into()));
user.sex = Set(enc_candidate.sex.map(|e| e.into()));
user.personal_identification_number = Set(enc_candidate.personal_id_number.map(|e| e.into()).unwrap_or_default()); // TODO: do not set this here, it is already set in the create_candidate mutation???
user.study = Set(enc_candidate.study.map(|e| e.into()));
user.updated_at = Set(chrono::offset::Local::now().naive_local());
let update = user.update(db).await?;
info!("CANDIDATE DETAILS ADDED");
info!("CANDIDATE {} DETAILS UPDATED", application);
Ok(update)
}
@ -139,7 +141,7 @@ mod tests {
vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()],
).await.unwrap();
Mutation::add_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap();
Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap();
let candidate = Query::find_candidate_by_id(&db, APPLICATION_ID)
.await

View file

@ -15,16 +15,22 @@ impl Mutation {
.await
}
pub async fn delete_parent(db: &DbConn, parent: Model) -> Result<DeleteResult, DbErr> {
parent
.delete(db)
.await
}
pub async fn add_parent_details(
db: &DbConn,
parent: Model,
enc_parent: EncryptedParentDetails,
) -> Result<Model, sea_orm::DbErr> {
let mut parent: parent::ActiveModel = parent.into();
parent.name = Set(Some(enc_parent.name.into()));
parent.surname = Set(Some(enc_parent.surname.into()));
parent.telephone = Set(Some(enc_parent.telephone.into()));
parent.email = Set(Some(enc_parent.email.into()));
parent.name = Set(enc_parent.name.map(|e| e.into()));
parent.surname = Set(enc_parent.surname.map(|e| e.into()));
parent.telephone = Set(enc_parent.telephone.map(|e| e.into()));
parent.email = Set(enc_parent.email.map(|e| e.into()));
parent.updated_at = Set(chrono::offset::Local::now().naive_local());

View file

@ -1,5 +1,5 @@
use chrono::{Utc, Duration, NaiveDateTime};
use ::entity::{session, candidate};
use ::entity::session;
use sea_orm::{*, prelude::Uuid};
use crate::Mutation;

View file

@ -5,7 +5,7 @@ use serde::{Serialize, Deserialize};
use crate::{error::ServiceError, database::query::candidate::CandidateResult, services::portfolio_service::SubmissionProgress};
use super::candidate_details::decrypt_if_exists;
use super::candidate_details::EncryptedString;
/// Minimal candidate response containing database only not null fields
#[derive(Debug, Serialize, Deserialize)]
@ -101,7 +101,7 @@ pub struct Row {
impl NewCandidateResponse {
pub async fn from_encrypted(private_key: &String, c: candidate::Model) -> Result<Self, ServiceError> {
let id_number = decrypt_if_exists(private_key, Some(c.personal_identification_number)).await?;
let id_number = EncryptedString::from(c.personal_identification_number).decrypt(private_key).await?;
Ok(
Self {
application_id: c.application,
@ -117,20 +117,20 @@ impl BaseCandidateResponse {
c: CandidateResult,
progress: Option<SubmissionProgress>,
) -> Result<Self, ServiceError> {
let name = decrypt_if_exists(private_key, c.name).await?;
let surname = decrypt_if_exists(private_key, c.surname).await?;
let email = decrypt_if_exists(private_key, c.email).await?;
let telephone = decrypt_if_exists(private_key, c.telephone).await?;
let progress = progress.unwrap_or(SubmissionProgress::NoneInCache);
let name = EncryptedString::decrypt_option(&EncryptedString::try_from(&c.name).ok(), private_key).await?;
let surname = EncryptedString::decrypt_option(&EncryptedString::try_from(&c.surname).ok(), private_key).await?;
let email = EncryptedString::decrypt_option(&EncryptedString::try_from(&c.email).ok(), private_key).await?;
let telephone = EncryptedString::decrypt_option(&EncryptedString::try_from(&c.telephone).ok(), private_key).await?;
Ok(
Self {
application_id: c.application,
name,
surname,
email,
telephone,
study: c.study.unwrap_or("".to_string()),
progress,
name: name.unwrap_or_default(),
surname: surname.unwrap_or_default(),
email: email.unwrap_or_default(),
telephone: telephone.unwrap_or_default(),
study: c.study.unwrap_or_default(),
progress: progress.unwrap_or(SubmissionProgress::NoneInCache),
}
)
}

View file

@ -14,25 +14,25 @@ pub struct EncryptedString(String);
#[derive(Debug, Clone)]
pub struct EncryptedCandidateDetails {
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 personal_id_number: EncryptedString,
pub study: String,
pub name: Option<EncryptedString>,
pub surname: Option<EncryptedString>,
pub birthplace: Option<EncryptedString>,
pub birthdate: Option<EncryptedString>,
pub address: Option<EncryptedString>,
pub telephone: Option<EncryptedString>,
pub citizenship: Option<EncryptedString>,
pub email: Option<EncryptedString>,
pub sex: Option<EncryptedString>,
pub personal_id_number: Option<EncryptedString>,
pub study: Option<String>,
}
#[derive(Debug, Clone)]
pub struct EncryptedParentDetails {
pub name: EncryptedString,
pub surname: EncryptedString,
pub telephone: EncryptedString,
pub email: EncryptedString,
pub name: Option<EncryptedString>,
pub surname: Option<EncryptedString>,
pub telephone: Option<EncryptedString>,
pub email: Option<EncryptedString>,
}
#[derive(Debug, Clone)]
pub struct EncryptedApplicationDetails {
@ -50,9 +50,16 @@ impl EncryptedString {
}
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),
crypto::decrypt_password_with_private_key(&self.0, private_key).await
}
pub async fn decrypt_option(
s: &Option<EncryptedString>,
private_key: &String,
) -> Result<Option<String>, ServiceError> {
match s {
Some(s) => Ok(Some(s.decrypt(private_key).await?)),
None => Ok(None),
}
}
@ -77,6 +84,7 @@ impl TryFrom<&Option<String>> for EncryptedString {
}
}
}
impl From<String> for EncryptedString {
fn from(s: String) -> Self {
Self(s)
@ -115,72 +123,68 @@ impl EncryptedCandidateDetails {
Ok(
EncryptedCandidateDetails {
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,
personal_id_number: d.9,
study: form.study.clone(),
name: Some(d.0),
surname: Some(d.1),
birthplace: Some(d.2),
birthdate: Some(d.3),
address: Some(d.4),
telephone: Some(d.5),
citizenship: Some(d.6),
email: Some(d.7),
sex: Some(d.8),
personal_id_number: Some(d.9),
study: Some(form.study.clone()),
}
)
}
pub async fn decrypt(self, priv_key: &String) -> Result<CandidateDetails, ServiceError> {
pub async fn decrypt(&self, priv_key: &String) -> Result<CandidateDetails, 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.personal_id_number.decrypt(priv_key),// 9
EncryptedString::decrypt_option(&self.name, priv_key), // 0
EncryptedString::decrypt_option(&self.surname, priv_key), // 1
EncryptedString::decrypt_option(&self.birthplace, priv_key), // 2
EncryptedString::decrypt_option(&self.birthdate, priv_key), // 3
EncryptedString::decrypt_option(&self.address, priv_key), // 4
EncryptedString::decrypt_option(&self.telephone, priv_key), // 5
EncryptedString::decrypt_option(&self.citizenship, priv_key), // 6
EncryptedString::decrypt_option(&self.email, priv_key), // 7
EncryptedString::decrypt_option(&self.sex, priv_key), // 8
EncryptedString::decrypt_option(&self.personal_id_number, priv_key),// 9
)?;
Ok(CandidateDetails {
name: d.0,
surname: d.1,
birthplace: d.2,
birthdate: NaiveDate::parse_from_str(&d.3, NAIVE_DATE_FMT).unwrap(),
address: d.4,
telephone: d.5,
citizenship: d.6,
email: d.7,
sex: d.8,
personal_id_number: d.9,
study: self.study,
name: d.0.unwrap_or_default(),
surname: d.1.unwrap_or_default(),
birthplace: d.2.unwrap_or_default(),
birthdate: NaiveDate::parse_from_str(&d.3.unwrap_or_default(), NAIVE_DATE_FMT).unwrap_or(NaiveDate::from_ymd(1, 1, 1)),
address: d.4.unwrap_or_default(),
telephone: d.5.unwrap_or_default(),
citizenship: d.6.unwrap_or_default(),
email: d.7.unwrap_or_default(),
sex: d.8.unwrap_or_default(),
personal_id_number: d.9.unwrap_or_default(),
study: self.study.clone().unwrap_or_default(),
}
)
}
}
impl TryFrom<&candidate::Model> for EncryptedCandidateDetails {
type Error = ServiceError;
fn try_from(
impl From<&candidate::Model> for EncryptedCandidateDetails {
fn from(
candidate: &candidate::Model,
) -> Result<Self, Self::Error> {
Ok(
EncryptedCandidateDetails {
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)?,
personal_id_number: EncryptedString::from(candidate.personal_identification_number.to_owned()),
study: candidate.study.as_ref().ok_or(ServiceError::CandidateDetailsNotSet)?.to_string(),
}
)
) -> Self {
EncryptedCandidateDetails {
name: EncryptedString::try_from(&candidate.name).ok(),
surname: EncryptedString::try_from(&candidate.surname).ok(),
birthplace: EncryptedString::try_from(&candidate.birthplace).ok(),
birthdate: EncryptedString::try_from(&candidate.birthdate).ok(),
address: EncryptedString::try_from(&candidate.address).ok(),
telephone: EncryptedString::try_from(&candidate.telephone).ok(),
citizenship: EncryptedString::try_from(&candidate.citizenship).ok(),
email: EncryptedString::try_from(&candidate.email).ok(),
sex: EncryptedString::try_from(&candidate.sex).ok(),
personal_id_number: Some(EncryptedString::from(candidate.personal_identification_number.to_owned())),
study: candidate.study.clone(),
}
}
}
@ -198,44 +202,41 @@ impl EncryptedParentDetails {
Ok(
EncryptedParentDetails {
name: d.0,
surname: d.1,
telephone: d.2,
email: d.3,
name: Some(d.0),
surname: Some(d.1),
telephone: Some(d.2),
email: Some(d.3),
}
)
}
pub async fn decrypt(&self, priv_key: &String) -> Result<ParentDetails, ServiceError> {
let d = tokio::try_join!(
self.name.decrypt(&priv_key),
self.surname.decrypt(&priv_key),
self.telephone.decrypt(&priv_key),
self.email.decrypt(&priv_key),
EncryptedString::decrypt_option(&self.name, &priv_key),
EncryptedString::decrypt_option(&self.surname, &priv_key),
EncryptedString::decrypt_option(&self.telephone, &priv_key),
EncryptedString::decrypt_option(&self.email, &priv_key),
)?;
Ok(ParentDetails {
name: d.0,
surname: d.1,
telephone: d.2,
email: d.3,
name: d.0.unwrap_or_default(),
surname: d.1.unwrap_or_default(),
telephone: d.2.unwrap_or_default(),
email: d.3.unwrap_or_default(),
}
)
}
}
impl TryFrom<&parent::Model> for EncryptedParentDetails {
type Error = ServiceError;
fn try_from(
impl From<&parent::Model> for EncryptedParentDetails {
fn from(
parent: &parent::Model,
) -> Result<Self, Self::Error> {
Ok(EncryptedParentDetails {
name: EncryptedString::try_from(&parent.name)?,
surname: EncryptedString::try_from(&parent.surname)?,
telephone: EncryptedString::try_from(&parent.telephone)?,
email: EncryptedString::try_from(&parent.email)?,
}
)
) -> Self {
EncryptedParentDetails {
name: EncryptedString::try_from(&parent.name).ok(),
surname: EncryptedString::try_from(&parent.surname).ok(),
telephone: EncryptedString::try_from(&parent.telephone).ok(),
email: EncryptedString::try_from(&parent.email).ok(),
}
}
}
@ -273,20 +274,18 @@ impl EncryptedApplicationDetails {
}
}
impl TryFrom<(&candidate::Model, Vec<parent::Model>)> for EncryptedApplicationDetails {
type Error = ServiceError;
fn try_from(
impl From<(&candidate::Model, Vec<parent::Model>)> for EncryptedApplicationDetails {
fn from(
(candidate, parents): (&candidate::Model, Vec<parent::Model>),
) -> Result<Self, Self::Error> {
) -> Self {
let enc_parents = parents.iter()
.map(|m| EncryptedParentDetails::try_from(m))
.collect::<Result<Vec<EncryptedParentDetails>, ServiceError>>()?;
.map(|m| EncryptedParentDetails::from(m))
.collect::<Vec<EncryptedParentDetails>>();
Ok(EncryptedApplicationDetails {
candidate: EncryptedCandidateDetails::try_from(candidate)?,
EncryptedApplicationDetails {
candidate: EncryptedCandidateDetails::from(candidate),
parents: enc_parents,
})
}
}
}
@ -298,30 +297,30 @@ impl TryFrom<Row> for EncryptedApplicationDetails {
) -> Result<Self, Self::Error> {
Ok(EncryptedApplicationDetails {
candidate: EncryptedCandidateDetails {
name: EncryptedString::try_from(&cp.name)?,
surname: EncryptedString::try_from(&cp.surname)?,
birthplace: EncryptedString::try_from(&cp.birthplace)?,
birthdate: EncryptedString::try_from(&cp.birthdate)?,
address: EncryptedString::try_from(&cp.address)?,
telephone: EncryptedString::try_from(&cp.telephone)?,
citizenship: EncryptedString::try_from(&cp.citizenship)?,
email: EncryptedString::try_from(&cp.email)?,
sex: EncryptedString::try_from(&cp.sex)?,
personal_id_number: EncryptedString::try_from(&cp.personal_identification_number)?,
study: cp.study.ok_or(ServiceError::CandidateDetailsNotSet)?,
name: EncryptedString::try_from(&cp.name).ok(),
surname: EncryptedString::try_from(&cp.surname).ok(),
birthplace: EncryptedString::try_from(&cp.birthplace).ok(),
birthdate: EncryptedString::try_from(&cp.birthdate).ok(),
address: EncryptedString::try_from(&cp.address).ok(),
telephone: EncryptedString::try_from(&cp.telephone).ok(),
citizenship: EncryptedString::try_from(&cp.citizenship).ok(),
email: EncryptedString::try_from(&cp.email).ok(),
sex: EncryptedString::try_from(&cp.sex).ok(),
personal_id_number: EncryptedString::try_from(&cp.personal_identification_number).ok(),
study: cp.study.ok_or(ServiceError::CandidateDetailsNotSet).ok(),
},
parents: vec![EncryptedParentDetails {
name: EncryptedString::try_from(&cp.parent_name)?,
surname: EncryptedString::try_from(&cp.parent_surname)?,
telephone: EncryptedString::try_from(&cp.parent_telephone)?,
email: EncryptedString::try_from(&cp.parent_email)?,
name: EncryptedString::try_from(&cp.parent_name).ok(),
surname: EncryptedString::try_from(&cp.parent_surname).ok(),
telephone: EncryptedString::try_from(&cp.parent_telephone).ok(),
email: EncryptedString::try_from(&cp.parent_email).ok(),
}]
})
}
}
pub async fn decrypt_if_exists(
/* pub async fn decrypt_if_exists(
private_key: &String,
encrypted_string: Option<String>,
) -> Result<String, ServiceError> {
@ -329,7 +328,7 @@ pub async fn decrypt_if_exists(
Ok(encrypted_string) => Ok(encrypted_string.decrypt(private_key).await?),
Err(_) => Ok(String::from("")),
}
}
} */
#[cfg(test)]
pub mod tests {
@ -419,19 +418,19 @@ pub mod tests {
.unwrap();
assert_eq!(
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.name.0, PRIVATE_KEY)
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.name.unwrap().0, PRIVATE_KEY)
.await
.unwrap(),
"name"
);
assert_eq!(
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.email.0, PRIVATE_KEY)
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.email.unwrap().0, PRIVATE_KEY)
.await
.unwrap(),
"email"
);
assert_eq!(
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.sex.0, PRIVATE_KEY)
crypto::decrypt_password_with_private_key(&encrypted_details.candidate.sex.unwrap().0, PRIVATE_KEY)
.await
.unwrap(),
"sex"

View file

@ -43,10 +43,11 @@ impl ApplicationService {
private_key: String,
db: &DbConn,
candidate: candidate::Model,
// parents: Vec<parent::Model>,
) -> Result<ApplicationDetails, ServiceError> {
println!("Decrypting all details: privkey: {}", private_key);
let parents = Query::find_candidate_parents(db, &candidate).await?;
let enc_details = EncryptedApplicationDetails::try_from((&candidate, parents))?;
let enc_details = EncryptedApplicationDetails::from((&candidate, parents));
enc_details.decrypt(private_key).await
}

View file

@ -10,35 +10,8 @@ use crate::{
Mutation, Query, models::{candidate::{BaseCandidateResponse, CreateCandidateResponse}, auth::AuthenticableTrait}, utils::db::get_recipients,
};
use super::{session_service::SessionService, application_service::ApplicationService, portfolio_service::PortfolioService};
use super::{session_service::SessionService, portfolio_service::PortfolioService};
// TODO validation
/* pub struct FieldOfStudy {
pub short_name: String,
pub full_name: String,
pub code: i32,
}
impl FieldOfStudy {
pub fn new(short_name: String, full_name: String, code: i32) -> Self {
Self {
short_name,
full_name,
code,
}
}
pub fn code_str(&self) -> String {
format!("{:04}", self.code)
}
}
pub enum FieldsOfStudy {
KB(FieldOfStudy),
IT(FieldOfStudy),
G(FieldOfStudy),
} */
const FIELD_OF_STUDY_PREFIXES: [&str; 3] = ["101", "102", "103"];
@ -111,36 +84,40 @@ impl CandidateService {
let new_password_hash = crypto::hash_password(new_password_plain.clone()).await?;
let (pubkey, priv_key_plain_text) = crypto::create_identity();
let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text,
let encrypted_priv_key = crypto::encrypt_password(priv_key_plain_text.clone(),
new_password_plain.to_string()
).await?;
Self::delete_old_sessions(db, &candidate, 0).await?;
let candidate = Mutation::update_candidate_password_and_keys(db,
candidate,
new_password_hash,
pubkey,
pubkey.clone(),
encrypted_priv_key
).await?;
// user might no have filled his details yet, but personal id number is filled from beginning
let personal_id_number = EncryptedString::from(candidate.personal_identification_number.clone())
.decrypt(&admin_private_key)
.await?;
let recipients = get_recipients(db, &pubkey).await?;
let enc_details_opt = EncryptedApplicationDetails::try_from(
(&candidate, parents)
);
if let Ok(enc_details) = enc_details_opt {
let application_details = enc_details.decrypt(admin_private_key).await?;
ApplicationService::add_all_details(db, candidate, &application_details).await?;
let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone()))
.decrypt(admin_private_key).await?;
let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?;
Mutation::update_candidate_details(db, candidate, enc_details.candidate).await?;
for i in 0..enc_details.parents.len() {
Mutation::add_parent_details(db, parents[i].clone(), enc_details.parents[i].clone()).await?;
}
Ok(
CreateCandidateResponse {
application_id: id,
personal_id_number: personal_id_number,
personal_id_number,
password: new_password_plain,
}
)
@ -160,7 +137,7 @@ impl CandidateService {
recipients: &Vec<String>,
) -> Result<entity::candidate::Model, ServiceError> {
let enc_details = EncryptedCandidateDetails::new(&details, recipients).await?;
let model = Mutation::add_candidate_details(db, candidate, enc_details).await?;
let model = Mutation::update_candidate_details(db, candidate, enc_details).await?;
Ok(model)
}

View file

@ -22,10 +22,11 @@ impl ParentService {
parents_details: &Vec<ParentDetails>,
recipients: &Vec<String>,
) -> Result<Vec<parent::Model>, ServiceError> {
let found_parents = Query::find_candidate_parents(db, ref_candidate).await?;
if found_parents.len() > 2 {
if parents_details.len() > 2 {
return Err(ServiceError::ParentOverflow);
}
let found_parents = Query::find_candidate_parents(db, ref_candidate).await?;
let mut result = vec![];
for i in 0..parents_details.len() {
@ -38,6 +39,11 @@ impl ParentService {
result.push(parent);
}
// delete parents that are not in the form
for i in parents_details.len()..found_parents.len() {
Mutation::delete_parent(db, found_parents[i].to_owned()).await?;
}
Ok(result)
}
}