From 2487efa01518e1fc7dc7e8da390e00ca2b16770f Mon Sep 17 00:00:00 2001 From: Stefan Melmuk Date: Sat, 10 Feb 2024 22:48:26 +0100 Subject: [PATCH] fix emergency access invites with no mail when mail is disabled instead of accepting emergency access for all invited users automatically, we only accept if the user already exists on registration of a new account any open emergency access invitations will be accepted, if mail is disabled also prevent invited emergency access contacts to register if emergency access is disabled (this is only relevant for when mail is enabled, if mail is disabled they should have an Invitation entry) --- src/api/core/accounts.rs | 12 +++++- src/api/core/emergency_access.rs | 61 +++++++------------------------ src/db/models/emergency_access.rs | 24 ++++++++++++ 3 files changed, 48 insertions(+), 49 deletions(-) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index da25d488..73e505a1 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -166,7 +166,8 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json } user } else if CONFIG.is_signup_allowed(&email) - || EmergencyAccess::find_invited_by_grantee_email(&email, &mut conn).await.is_some() + || (CONFIG.emergency_access_allowed() + && EmergencyAccess::find_invited_by_grantee_email(&email, &mut conn).await.is_some()) { user } else { @@ -217,7 +218,6 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json if let Err(e) = mail::send_welcome_must_verify(&user.email, &user.uuid).await { error!("Error sending welcome email: {:#?}", e); } - user.last_verifying_at = Some(user.created_at); } else if let Err(e) = mail::send_welcome(&user.email).await { error!("Error sending welcome email: {:#?}", e); @@ -229,6 +229,14 @@ pub async fn _register(data: JsonUpcase, mut conn: DbConn) -> Json } user.save(&mut conn).await?; + + // accept any open emergency access invitations + if !CONFIG.mail_enabled() && CONFIG.emergency_access_allowed() { + for mut emergency_invite in EmergencyAccess::find_all_invited_by_grantee_email(&user.email, &mut conn).await { + let _ = emergency_invite.accept_invite(&user.uuid, &user.email, &mut conn).await; + } + } + Ok(Json(json!({ "Object": "register", "CaptchaBypassToken": "", diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 5f64cde0..5cc473fc 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -209,7 +209,7 @@ async fn send_invite(data: JsonUpcase, headers: Heade err!("You can not set yourself as an emergency contact.") } - let grantee_user = match User::find_by_mail(&email, &mut conn).await { + let (grantee_user, new_user) = match User::find_by_mail(&email, &mut conn).await { None => { if !CONFIG.invitations_allowed() { err!(format!("Grantee user does not exist: {}", &email)) @@ -226,9 +226,10 @@ async fn send_invite(data: JsonUpcase, headers: Heade let mut user = User::new(email.clone()); user.save(&mut conn).await?; - user + (user, true) } - Some(user) => user, + Some(user) if user.password_hash.is_empty() => (user, true), + Some(user) => (user, false), }; if EmergencyAccess::find_by_grantor_uuid_and_grantee_uuid_or_email( @@ -256,15 +257,9 @@ async fn send_invite(data: JsonUpcase, headers: Heade &grantor_user.email, ) .await?; - } else { - // Automatically mark user as accepted if no email invites - match User::find_by_mail(&email, &mut conn).await { - Some(user) => match accept_invite_process(&user.uuid, &mut new_emergency_access, &email, &mut conn).await { - Ok(v) => v, - Err(e) => err!(e.to_string()), - }, - None => err!("Grantee user not found."), - } + } else if !new_user { + // if mail is not enabled immediately accept the invitation for existing users + new_emergency_access.accept_invite(&grantee_user.uuid, &email, &mut conn).await?; } Ok(()) @@ -308,17 +303,12 @@ async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> Emp &grantor_user.email, ) .await?; - } else { - if Invitation::find_by_mail(&email, &mut conn).await.is_none() { - let invitation = Invitation::new(&email); - invitation.save(&mut conn).await?; - } - - // Automatically mark user as accepted if no email invites - match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &email, &mut conn).await { - Ok(v) => v, - Err(e) => err!(e.to_string()), - } + } else if !grantee_user.password_hash.is_empty() { + // accept the invitation for existing user + emergency_access.accept_invite(&grantee_user.uuid, &email, &mut conn).await?; + } else if CONFIG.invitations_allowed() && Invitation::find_by_mail(&email, &mut conn).await.is_none() { + let invitation = Invitation::new(&email); + invitation.save(&mut conn).await?; } Ok(()) @@ -367,10 +357,7 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Hea && grantor_user.name == claims.grantor_name && grantor_user.email == claims.grantor_email { - match accept_invite_process(&grantee_user.uuid, &mut emergency_access, &grantee_user.email, &mut conn).await { - Ok(v) => v, - Err(e) => err!(e.to_string()), - } + emergency_access.accept_invite(&grantee_user.uuid, &grantee_user.email, &mut conn).await?; if CONFIG.mail_enabled() { mail::send_emergency_access_invite_accepted(&grantor_user.email, &grantee_user.email).await?; @@ -382,26 +369,6 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase, headers: Hea } } -async fn accept_invite_process( - grantee_uuid: &str, - emergency_access: &mut EmergencyAccess, - grantee_email: &str, - conn: &mut DbConn, -) -> EmptyResult { - if emergency_access.email.is_none() || emergency_access.email.as_ref().unwrap() != grantee_email { - err!("User email does not match invite."); - } - - if emergency_access.status == EmergencyAccessStatus::Accepted as i32 { - err!("Emergency contact already accepted."); - } - - emergency_access.status = EmergencyAccessStatus::Accepted as i32; - emergency_access.grantee_uuid = Some(String::from(grantee_uuid)); - emergency_access.email = None; - emergency_access.save(conn).await -} - #[derive(Deserialize)] #[allow(non_snake_case)] struct ConfirmData { diff --git a/src/db/models/emergency_access.rs b/src/db/models/emergency_access.rs index ccb21e5b..3fb0c4c7 100644 --- a/src/db/models/emergency_access.rs +++ b/src/db/models/emergency_access.rs @@ -285,6 +285,15 @@ impl EmergencyAccess { }} } + pub async fn find_all_invited_by_grantee_email(grantee_email: &str, conn: &mut DbConn) -> Vec { + db_run! { conn: { + emergency_access::table + .filter(emergency_access::email.eq(grantee_email)) + .filter(emergency_access::status.eq(EmergencyAccessStatus::Invited as i32)) + .load::(conn).expect("Error loading emergency_access").from_db() + }} + } + pub async fn find_all_by_grantor_uuid(grantor_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { emergency_access::table @@ -292,6 +301,21 @@ impl EmergencyAccess { .load::(conn).expect("Error loading emergency_access").from_db() }} } + + pub async fn accept_invite(&mut self, grantee_uuid: &str, grantee_email: &str, conn: &mut DbConn) -> EmptyResult { + if self.email.is_none() || self.email.as_ref().unwrap() != grantee_email { + err!("User email does not match invite."); + } + + if self.status == EmergencyAccessStatus::Accepted as i32 { + err!("Emergency contact already accepted."); + } + + self.status = EmergencyAccessStatus::Accepted as i32; + self.grantee_uuid = Some(String::from(grantee_uuid)); + self.email = None; + self.save(conn).await + } } // endregion