refactor: encrypted user details

This commit is contained in:
Sebastian Pravda 2022-11-11 18:44:42 +01:00
parent cfb2fb047b
commit e3a37dd59b
No known key found for this signature in database
GPG key ID: F3BC84F08EFA3F57
2 changed files with 105 additions and 103 deletions

View file

@ -32,15 +32,15 @@ impl Mutation {
enc_details: EncryptedUserDetails, enc_details: EncryptedUserDetails,
) -> 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.address = Set(Some(enc_details.address.into()));
user.telephone = Set(Some(enc_details.telephone)); user.telephone = Set(Some(enc_details.telephone.into()));
user.citizenship = Set(Some(enc_details.citizenship)); user.citizenship = Set(Some(enc_details.citizenship.into()));
user.email = Set(Some(enc_details.email)); user.email = Set(Some(enc_details.email.into()));
user.sex = Set(Some(enc_details.sex)); user.sex = Set(Some(enc_details.sex.into()));
user.study = Set(Some(enc_details.study)); 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

@ -12,21 +12,60 @@ 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 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 async 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),
}
}
}
pub(crate) struct EncryptedUserDetails { pub(crate) struct EncryptedUserDetails {
pub name: String, pub name: EncryptedString,
pub surname: String, pub surname: EncryptedString,
pub birthplace: String, pub birthplace: EncryptedString,
// pub birthdate: NaiveDate, // pub birthdate: NaiveDate,
pub address: String, pub address: EncryptedString,
pub telephone: String, pub telephone: EncryptedString,
pub citizenship: String, pub citizenship: EncryptedString,
pub email: String, pub email: EncryptedString,
pub sex: String, pub sex: EncryptedString,
pub study: String, pub study: EncryptedString,
} }
impl EncryptedUserDetails { impl EncryptedUserDetails {
pub async fn encrypt_form(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails { pub async fn new(form: UserDetails, recipients: Vec<&str>) -> EncryptedUserDetails {
let ( let (
Ok(name), Ok(name),
Ok(surname), Ok(surname),
@ -39,16 +78,16 @@ impl EncryptedUserDetails {
Ok(sex), Ok(sex),
Ok(study), Ok(study),
) = tokio::join!( ) = tokio::join!(
crypto::encrypt_password_with_recipients(&form.name, &recipients), EncryptedString::new(&form.name, &recipients),
crypto::encrypt_password_with_recipients(&form.surname, &recipients), EncryptedString::new(&form.surname, &recipients),
crypto::encrypt_password_with_recipients(&form.birthplace, &recipients), EncryptedString::new(&form.birthplace, &recipients),
// crypto::encrypt_password_with_recipients(&self.birthdate, &recipients), // TODO // EncryptedString::new((&self.birthdate, &recipients), // TODO
crypto::encrypt_password_with_recipients(&form.address, &recipients), EncryptedString::new(&form.address, &recipients),
crypto::encrypt_password_with_recipients(&form.telephone, &recipients), EncryptedString::new(&form.telephone, &recipients),
crypto::encrypt_password_with_recipients(&form.citizenship, &recipients), EncryptedString::new(&form.citizenship, &recipients),
crypto::encrypt_password_with_recipients(&form.email, &recipients), EncryptedString::new(&form.email, &recipients),
crypto::encrypt_password_with_recipients(&form.sex, &recipients), EncryptedString::new(&form.sex, &recipients),
crypto::encrypt_password_with_recipients(&form.study, &recipients), EncryptedString::new(&form.study, &recipients),
) else { ) else {
panic!("Failed to encrypt user details"); // TODO panic!("Failed to encrypt user details"); // TODO
}; };
@ -67,67 +106,6 @@ impl EncryptedUserDetails {
} }
} }
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> { pub async fn decrypt(self, priv_key: String) -> Result<UserDetails, ServiceError> {
let ( let (
Ok(name), Ok(name),
@ -141,15 +119,16 @@ impl EncryptedUserDetails {
Ok(sex), Ok(sex),
Ok(study), Ok(study),
) = tokio::join!( ) = tokio::join!(
crypto::decrypt_password_with_private_key(&self.name, &priv_key), self.name.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.surname, &priv_key), self.surname.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.birthplace, &priv_key), self.birthplace.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.address, &priv_key), // self.birthdate.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.telephone, &priv_key), self.address.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.citizenship, &priv_key), self.telephone.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.email, &priv_key), self.citizenship.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.sex, &priv_key), self.email.decrypt(&priv_key),
crypto::decrypt_password_with_private_key(&self.study, &priv_key), self.sex.decrypt(&priv_key),
self.study.decrypt(&priv_key),
) else { ) else {
panic!("Failed to encrypt user details"); // TODO panic!("Failed to encrypt user details"); // TODO
}; };
@ -169,6 +148,29 @@ impl EncryptedUserDetails {
} }
} }
impl TryFrom<candidate::Model> for EncryptedUserDetails {
type Error = ServiceError;
fn try_from(candidate: candidate::Model) -> Result<Self, Self::Error> {
if !CandidateService::is_set_up(&candidate) {
return Err(ServiceError::CandidateDetailsNotSet);
}
Ok(EncryptedUserDetails {
name: EncryptedString::try_from(candidate.name)?,
surname: EncryptedString::try_from(candidate.surname)?,
birthplace: EncryptedString::try_from(candidate.birthplace)?,
// 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)?,
})
}
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct UserDetails { pub struct UserDetails {
pub name: String, pub name: String,
@ -256,7 +258,7 @@ impl CandidateService {
recipients.append(&mut admin_public_keys_refrence); recipients.append(&mut admin_public_keys_refrence);
let enc_details = EncryptedUserDetails::encrypt_form(form, recipients).await; let enc_details = EncryptedUserDetails::new(form, recipients).await;
Mutation::add_candidate_details(db, user, enc_details) Mutation::add_candidate_details(db, user, enc_details)
.await .await
@ -286,12 +288,12 @@ impl CandidateService {
.await .await
.ok() .ok()
.unwrap(); .unwrap();
let enc_details = EncryptedUserDetails::from_model(candidate)?; let enc_details = EncryptedUserDetails::try_from(candidate)?;
enc_details.decrypt(dec_priv_key).await 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() &&
@ -505,7 +507,7 @@ mod tests {
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 dec_candidate = EncryptedUserDetails::try_from(enc_candidate)
.unwrap() .unwrap()
.decrypt(dec_priv_key) .decrypt(dec_priv_key)
.await .await