feat!: lock candidate details for application entities. who can't decrypt it

This commit is contained in:
Sebastian Pravda 2023-01-14 17:33:40 +01:00
parent fc176348d0
commit 85c6a47232
No known key found for this signature in database
GPG key ID: F3BC84F08EFA3F57
9 changed files with 48 additions and 24 deletions

View file

@ -166,13 +166,12 @@ pub async fn get_candidate(
.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,
db,
candidate
&application,
false,
)
.await
.map_err(to_custom_error)?;
@ -194,7 +193,7 @@ pub async fn delete_candidate(
.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 candidate = ApplicationService::find_related_candidate(db, &application).await.map_err(to_custom_error)?;
// TODO

View file

@ -77,7 +77,7 @@ pub async fn whoami(conn: Connection<'_, Db>, session: ApplicationAuth) -> Resul
let private_key = session.get_private_key();
let application: entity::application::Model = session.into();
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO
println!("candidate: {:?}", candidate);
let response = NewCandidateResponse::from_encrypted(&private_key, candidate).await
.map_err(to_custom_error)?;
@ -95,9 +95,9 @@ pub async fn post_details(
let db = conn.into_inner();
let form = details.into_inner();
let application: application::Model = session.into();
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO
let _candidate_parent = ApplicationService::add_all_details(db, &application.public_key, candidate, &form)
let _candidate_parent = ApplicationService::add_all_details(db, &application, candidate, &form)
.await
.map_err(to_custom_error)?;
@ -112,9 +112,13 @@ pub async fn get_details(
let db = conn.into_inner();
let private_key = session.get_private_key();
let application: entity::application::Model = session.into();
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
let details = ApplicationService::decrypt_all_details(private_key, db, candidate)
let details = ApplicationService::decrypt_all_details(
private_key,
db,
&application,
true
)
.await
.map(|x| Json(x))
.map_err(to_custom_error);
@ -218,7 +222,7 @@ pub async fn submit_portfolio(
let db = conn.into_inner();
let application: entity::application::Model = session.into();
let candidate = ApplicationService::find_related_candidate(&db, application.clone()).await.map_err(to_custom_error)?; // TODO
let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO
let submit = PortfolioService::submit(&application.public_key, &candidate, &db).await;

View file

@ -44,11 +44,12 @@ impl Mutation {
pub async fn update_candidate_details(
db: &DbConn,
user: candidate::Model,
candidate: candidate::Model,
enc_candidate: EncryptedCandidateDetails,
encrypted_by_id: i32,
) -> Result<candidate::Model, sea_orm::DbErr> {
let application = user.id;
let mut candidate: candidate::ActiveModel = user.into();
let application = candidate.id;
let mut candidate: candidate::ActiveModel = candidate.into();
candidate.name = Set(enc_candidate.name.map(|e| e.into()));
candidate.surname = Set(enc_candidate.surname.map(|e| e.into()));
@ -63,6 +64,7 @@ impl Mutation {
candidate.school_name = Set(enc_candidate.school_name.map(|e| e.into()));
candidate.health_insurance = Set(enc_candidate.health_insurance.map(|e| e.into()));
candidate.study = Set(enc_candidate.study.map(|e| e.into()));
candidate.encrypted_by_id = Set(Some(encrypted_by_id));
candidate.updated_at = Set(chrono::offset::Local::now().naive_local());
@ -128,7 +130,7 @@ mod tests {
vec!["age1u889gp407hsz309wn09kxx9anl6uns30m27lfwnctfyq9tq4qpus8tzmq5".to_string()],
).await.unwrap();
let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate).await.unwrap();
let candidate = Mutation::update_candidate_details(&db, candidate, encrypted_details.candidate, 1).await.unwrap();
let candidate = Query::find_candidate_by_id(&db, candidate.id)
.await

View file

@ -27,7 +27,7 @@ impl Query {
pub async fn find_related_candidate(
db: &DbConn,
application: application::Model,
application: &application::Model,
) -> Result<Option<candidate::Model>, DbErr> {
application
.find_related(candidate::Entity)

View file

@ -18,6 +18,8 @@ pub enum ServiceError {
UserAlreadyExists,
#[error("Candidate not found")]
CandidateNotFound,
#[error("Resource is locked")]
Locked,
#[error("Parrent not found")]
ParentNotFound,
#[error("Database error")]
@ -69,7 +71,7 @@ pub enum ServiceError {
impl ServiceError {
pub fn code(&self) -> u16 {
match self {
// 40X
// 4XX
ServiceError::InvalidApplicationId => 400,
ServiceError::ParentOverflow => 400,
ServiceError::Unauthorized => 401,
@ -79,6 +81,7 @@ impl ServiceError {
ServiceError::CandidateNotFound => 404,
ServiceError::IncompletePortfolio => 406,
ServiceError::UserAlreadyExists => 409,
ServiceError::Locked => 423,
// 500
ServiceError::ParentNotFound => 500,
ServiceError::DbError(_) => 500,

View file

@ -160,7 +160,7 @@ impl ApplicationService {
pub async fn find_related_candidate(
db: &DbConn,
application: application::Model,
application: &application::Model,
) -> Result<candidate::Model, ServiceError> {
let candidate = Query::find_related_candidate(db, application).await?;
if let Some(candidate) = candidate {
@ -172,13 +172,13 @@ impl ApplicationService {
pub async fn add_all_details(
db: &DbConn,
public_key: &String,
application: &application::Model,
candidate: candidate::Model,
form: &ApplicationDetails,
) -> Result<(candidate::Model, Vec<parent::Model>), ServiceError> {
let recipients = get_recipients(db, public_key).await?;
let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients).await?;
let recipients = get_recipients(db, &application.public_key).await?;
let candidate = CandidateService::add_candidate_details(db, candidate, &form.candidate, &recipients, application.id).await?;
let parents = ParentService::add_parents_details(db, &candidate, &form.parents, &recipients).await?;
Ok(
(
@ -191,8 +191,15 @@ impl ApplicationService {
pub async fn decrypt_all_details(
private_key: String,
db: &DbConn,
candidate: candidate::Model,
application: &application::Model,
restrict_access: bool,
) -> Result<ApplicationDetails, ServiceError> {
let candidate = ApplicationService::find_related_candidate(db, application).await?;
if restrict_access && candidate.encrypted_by_id.is_some() && candidate.encrypted_by_id != Some(application.id) {
return Err(ServiceError::Locked)
}
let parents = Query::find_candidate_parents(db, &candidate).await?;
let enc_details = EncryptedApplicationDetails::from((&candidate, parents));

View file

@ -48,9 +48,15 @@ impl CandidateService {
candidate: candidate::Model,
details: &CandidateDetails,
recipients: &Vec<String>,
encrypted_by: i32,
) -> Result<entity::candidate::Model, ServiceError> {
let enc_details = EncryptedCandidateDetails::new(&details, recipients).await?;
let model = Mutation::update_candidate_details(db, candidate, enc_details).await?;
let model = Mutation::update_candidate_details(
db,
candidate,
enc_details,
encrypted_by
).await?;
Ok(model)
}
@ -148,11 +154,11 @@ pub mod tests {
"0000001111".to_string()
).await.unwrap();
let candidate= ApplicationService::find_related_candidate(db, application.to_owned()).await.unwrap();
let candidate= ApplicationService::find_related_candidate(db, &application).await.unwrap();
let form = APPLICATION_DETAILS.lock().unwrap().clone();
let (candidate, parents) = ApplicationService::add_all_details(&db, &application.public_key, candidate, &form)
let (candidate, parents) = ApplicationService::add_all_details(&db, &application, candidate, &form)
.await
.unwrap();

View file

@ -21,6 +21,7 @@ pub struct Model {
pub personal_identification_number: String,
pub school_name: Option<String>,
pub health_insurance: Option<String>,
pub encrypted_by_id: Option<i32>,
pub created_at: DateTime,
pub updated_at: DateTime,
}

View file

@ -32,6 +32,7 @@ impl MigrationTrait for Migration {
.col(ColumnDef::new(Candidate::PersonalIdentificationNumber).string().not_null())
.col(ColumnDef::new(Candidate::SchoolName).string())
.col(ColumnDef::new(Candidate::HealthInsurance).string())
.col(ColumnDef::new(Candidate::EncryptedById).integer())
.col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null())
.col(ColumnDef::new(Candidate::UpdatedAt).date_time().not_null())
.to_owned(),
@ -64,6 +65,7 @@ pub enum Candidate {
PersonalIdentificationNumber,
SchoolName,
HealthInsurance,
EncryptedById,
CreatedAt,
UpdatedAt,
}