|
@@ -1,6 +1,5 @@
|
|
|
use chrono::{Duration, Utc};
|
|
|
-use rocket::serde::json::Json;
|
|
|
-use rocket::Route;
|
|
|
+use rocket::{serde::json::Json, Route};
|
|
|
use serde_json::Value;
|
|
|
|
|
|
use crate::{
|
|
@@ -41,9 +40,10 @@ pub fn routes() -> Vec<Route> {
|
|
|
async fn get_contacts(headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let mut emergency_access_list_json = Vec::new();
|
|
|
- for e in EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &mut conn).await {
|
|
|
- emergency_access_list_json.push(e.to_json_grantee_details(&mut conn).await);
|
|
|
+ let emergency_access_list = EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &mut conn).await;
|
|
|
+ let mut emergency_access_list_json = Vec::with_capacity(emergency_access_list.len());
|
|
|
+ for ea in emergency_access_list {
|
|
|
+ emergency_access_list_json.push(ea.to_json_grantee_details(&mut conn).await);
|
|
|
}
|
|
|
|
|
|
Ok(Json(json!({
|
|
@@ -57,9 +57,10 @@ async fn get_contacts(headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
async fn get_grantees(headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let mut emergency_access_list_json = Vec::new();
|
|
|
- for e in EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &mut conn).await {
|
|
|
- emergency_access_list_json.push(e.to_json_grantor_details(&mut conn).await);
|
|
|
+ let emergency_access_list = EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &mut conn).await;
|
|
|
+ let mut emergency_access_list_json = Vec::with_capacity(emergency_access_list.len());
|
|
|
+ for ea in emergency_access_list {
|
|
|
+ emergency_access_list_json.push(ea.to_json_grantor_details(&mut conn).await);
|
|
|
}
|
|
|
|
|
|
Ok(Json(json!({
|
|
@@ -83,7 +84,7 @@ async fn get_emergency_access(emer_id: String, mut conn: DbConn) -> JsonResult {
|
|
|
|
|
|
// region put/post
|
|
|
|
|
|
-#[derive(Deserialize, Debug)]
|
|
|
+#[derive(Deserialize)]
|
|
|
#[allow(non_snake_case)]
|
|
|
struct EmergencyAccessUpdateData {
|
|
|
Type: NumberOrString,
|
|
@@ -160,7 +161,7 @@ async fn post_delete_emergency_access(emer_id: String, headers: Headers, conn: D
|
|
|
|
|
|
// region invite
|
|
|
|
|
|
-#[derive(Deserialize, Debug)]
|
|
|
+#[derive(Deserialize)]
|
|
|
#[allow(non_snake_case)]
|
|
|
struct EmergencyAccessInviteData {
|
|
|
Email: String,
|
|
@@ -193,7 +194,7 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
|
|
|
let grantee_user = match User::find_by_mail(&email, &mut conn).await {
|
|
|
None => {
|
|
|
if !CONFIG.invitations_allowed() {
|
|
|
- err!(format!("Grantee user does not exist: {}", email))
|
|
|
+ err!(format!("Grantee user does not exist: {}", &email))
|
|
|
}
|
|
|
|
|
|
if !CONFIG.is_email_domain_allowed(&email) {
|
|
@@ -201,7 +202,7 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
|
|
|
}
|
|
|
|
|
|
if !CONFIG.mail_enabled() {
|
|
|
- let invitation = Invitation::new(email.clone());
|
|
|
+ let invitation = Invitation::new(&email);
|
|
|
invitation.save(&mut conn).await?;
|
|
|
}
|
|
|
|
|
@@ -221,36 +222,29 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
|
|
|
.await
|
|
|
.is_some()
|
|
|
{
|
|
|
- err!(format!("Grantee user already invited: {}", email))
|
|
|
+ err!(format!("Grantee user already invited: {}", &grantee_user.email))
|
|
|
}
|
|
|
|
|
|
- let mut new_emergency_access = EmergencyAccess::new(
|
|
|
- grantor_user.uuid.clone(),
|
|
|
- Some(grantee_user.email.clone()),
|
|
|
- emergency_access_status,
|
|
|
- new_type,
|
|
|
- wait_time_days,
|
|
|
- );
|
|
|
+ let mut new_emergency_access =
|
|
|
+ EmergencyAccess::new(grantor_user.uuid, grantee_user.email, emergency_access_status, new_type, wait_time_days);
|
|
|
new_emergency_access.save(&mut conn).await?;
|
|
|
|
|
|
if CONFIG.mail_enabled() {
|
|
|
mail::send_emergency_access_invite(
|
|
|
- &grantee_user.email,
|
|
|
+ &new_emergency_access.email.expect("Grantee email does not exists"),
|
|
|
&grantee_user.uuid,
|
|
|
- Some(new_emergency_access.uuid),
|
|
|
- Some(grantor_user.name.clone()),
|
|
|
- Some(grantor_user.email),
|
|
|
+ &new_emergency_access.uuid,
|
|
|
+ &grantor_user.name,
|
|
|
+ &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, new_emergency_access.uuid, Some(email), &mut conn).await {
|
|
|
- Ok(v) => v,
|
|
|
- Err(e) => err!(e.to_string()),
|
|
|
- }
|
|
|
- }
|
|
|
+ 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."),
|
|
|
}
|
|
|
}
|
|
@@ -262,7 +256,7 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
|
|
|
async fn resend_invite(emer_id: String, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
+ let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
Some(emer) => emer,
|
|
|
None => err!("Emergency access not valid."),
|
|
|
};
|
|
@@ -291,19 +285,19 @@ async fn resend_invite(emer_id: String, headers: Headers, mut conn: DbConn) -> E
|
|
|
mail::send_emergency_access_invite(
|
|
|
&email,
|
|
|
&grantor_user.uuid,
|
|
|
- Some(emergency_access.uuid),
|
|
|
- Some(grantor_user.name.clone()),
|
|
|
- Some(grantor_user.email),
|
|
|
+ &emergency_access.uuid,
|
|
|
+ &grantor_user.name,
|
|
|
+ &grantor_user.email,
|
|
|
)
|
|
|
.await?;
|
|
|
} else {
|
|
|
if Invitation::find_by_mail(&email, &mut conn).await.is_none() {
|
|
|
- let invitation = Invitation::new(email);
|
|
|
+ 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, emergency_access.uuid, emergency_access.email, &mut conn).await {
|
|
|
+ match accept_invite_process(grantee_user.uuid, &mut emergency_access, &email, &mut conn).await {
|
|
|
Ok(v) => v,
|
|
|
Err(e) => err!(e.to_string()),
|
|
|
}
|
|
@@ -319,13 +313,24 @@ struct AcceptData {
|
|
|
}
|
|
|
|
|
|
#[post("/emergency-access/<emer_id>/accept", data = "<data>")]
|
|
|
-async fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, mut conn: DbConn) -> EmptyResult {
|
|
|
+async fn accept_invite(
|
|
|
+ emer_id: String,
|
|
|
+ data: JsonUpcase<AcceptData>,
|
|
|
+ headers: Headers,
|
|
|
+ mut conn: DbConn,
|
|
|
+) -> EmptyResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
let data: AcceptData = data.into_inner().data;
|
|
|
let token = &data.Token;
|
|
|
let claims = decode_emergency_access_invite(token)?;
|
|
|
|
|
|
+ // This can happen if the user who received the invite used a different email to signup.
|
|
|
+ // Since we do not know if this is intented, we error out here and do nothing with the invite.
|
|
|
+ if claims.email != headers.user.email {
|
|
|
+ err!("Claim email does not match current users email")
|
|
|
+ }
|
|
|
+
|
|
|
let grantee_user = match User::find_by_mail(&claims.email, &mut conn).await {
|
|
|
Some(user) => {
|
|
|
Invitation::take(&claims.email, &mut conn).await;
|
|
@@ -334,7 +339,7 @@ async fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, mut conn:
|
|
|
None => err!("Invited user not found"),
|
|
|
};
|
|
|
|
|
|
- let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
+ let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
Some(emer) => emer,
|
|
|
None => err!("Emergency access not valid."),
|
|
|
};
|
|
@@ -345,13 +350,11 @@ async fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, mut conn:
|
|
|
None => err!("Grantor user not found."),
|
|
|
};
|
|
|
|
|
|
- if (claims.emer_id.is_some() && emer_id == claims.emer_id.unwrap())
|
|
|
- && (claims.grantor_name.is_some() && grantor_user.name == claims.grantor_name.unwrap())
|
|
|
- && (claims.grantor_email.is_some() && grantor_user.email == claims.grantor_email.unwrap())
|
|
|
+ if emer_id == claims.emer_id
|
|
|
+ && grantor_user.name == claims.grantor_name
|
|
|
+ && grantor_user.email == claims.grantor_email
|
|
|
{
|
|
|
- match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &mut conn)
|
|
|
- .await
|
|
|
- {
|
|
|
+ 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()),
|
|
|
}
|
|
@@ -368,17 +371,11 @@ async fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, mut conn:
|
|
|
|
|
|
async fn accept_invite_process(
|
|
|
grantee_uuid: String,
|
|
|
- emer_id: String,
|
|
|
- email: Option<String>,
|
|
|
+ emergency_access: &mut EmergencyAccess,
|
|
|
+ grantee_email: &str,
|
|
|
conn: &mut DbConn,
|
|
|
) -> EmptyResult {
|
|
|
- let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, conn).await {
|
|
|
- Some(emer) => emer,
|
|
|
- None => err!("Emergency access not valid."),
|
|
|
- };
|
|
|
-
|
|
|
- let emer_email = emergency_access.email;
|
|
|
- if emer_email.is_none() || emer_email != email {
|
|
|
+ if emergency_access.email.is_none() || emergency_access.email.as_ref().unwrap() != grantee_email {
|
|
|
err!("User email does not match invite.");
|
|
|
}
|
|
|
|
|
@@ -463,7 +460,7 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, mut conn:
|
|
|
};
|
|
|
|
|
|
if emergency_access.status != EmergencyAccessStatus::Confirmed as i32
|
|
|
- || emergency_access.grantee_uuid != Some(initiating_user.uuid.clone())
|
|
|
+ || emergency_access.grantee_uuid != Some(initiating_user.uuid)
|
|
|
{
|
|
|
err!("Emergency access not valid.")
|
|
|
}
|
|
@@ -485,7 +482,7 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, mut conn:
|
|
|
&grantor_user.email,
|
|
|
&initiating_user.name,
|
|
|
emergency_access.get_type_as_str(),
|
|
|
- &emergency_access.wait_time_days.clone().to_string(),
|
|
|
+ &emergency_access.wait_time_days,
|
|
|
)
|
|
|
.await?;
|
|
|
}
|
|
@@ -496,19 +493,18 @@ async fn initiate_emergency_access(emer_id: String, headers: Headers, mut conn:
|
|
|
async fn approve_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let approving_user = headers.user;
|
|
|
let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
Some(emer) => emer,
|
|
|
None => err!("Emergency access not valid."),
|
|
|
};
|
|
|
|
|
|
if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
|
|
|
- || emergency_access.grantor_uuid != approving_user.uuid
|
|
|
+ || emergency_access.grantor_uuid != headers.user.uuid
|
|
|
{
|
|
|
err!("Emergency access not valid.")
|
|
|
}
|
|
|
|
|
|
- let grantor_user = match User::find_by_uuid(&approving_user.uuid, &mut conn).await {
|
|
|
+ let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await {
|
|
|
Some(user) => user,
|
|
|
None => err!("Grantor user not found."),
|
|
|
};
|
|
@@ -535,7 +531,6 @@ async fn approve_emergency_access(emer_id: String, headers: Headers, mut conn: D
|
|
|
async fn reject_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let rejecting_user = headers.user;
|
|
|
let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
Some(emer) => emer,
|
|
|
None => err!("Emergency access not valid."),
|
|
@@ -543,12 +538,12 @@ async fn reject_emergency_access(emer_id: String, headers: Headers, mut conn: Db
|
|
|
|
|
|
if (emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
|
|
|
&& emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32)
|
|
|
- || emergency_access.grantor_uuid != rejecting_user.uuid
|
|
|
+ || emergency_access.grantor_uuid != headers.user.uuid
|
|
|
{
|
|
|
err!("Emergency access not valid.")
|
|
|
}
|
|
|
|
|
|
- let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &mut conn).await {
|
|
|
+ let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await {
|
|
|
Some(user) => user,
|
|
|
None => err!("Grantor user not found."),
|
|
|
};
|
|
@@ -579,14 +574,12 @@ async fn reject_emergency_access(emer_id: String, headers: Headers, mut conn: Db
|
|
|
async fn view_emergency_access(emer_id: String, headers: Headers, mut conn: DbConn) -> JsonResult {
|
|
|
check_emergency_access_allowed()?;
|
|
|
|
|
|
- let requesting_user = headers.user;
|
|
|
- let host = headers.host;
|
|
|
let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &mut conn).await {
|
|
|
Some(emer) => emer,
|
|
|
None => err!("Emergency access not valid."),
|
|
|
};
|
|
|
|
|
|
- if !is_valid_request(&emergency_access, requesting_user.uuid, EmergencyAccessType::View) {
|
|
|
+ if !is_valid_request(&emergency_access, headers.user.uuid, EmergencyAccessType::View) {
|
|
|
err!("Emergency access not valid.")
|
|
|
}
|
|
|
|
|
@@ -596,7 +589,8 @@ async fn view_emergency_access(emer_id: String, headers: Headers, mut conn: DbCo
|
|
|
|
|
|
let mut ciphers_json = Vec::new();
|
|
|
for c in ciphers {
|
|
|
- ciphers_json.push(c.to_json(&host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &mut conn).await);
|
|
|
+ ciphers_json
|
|
|
+ .push(c.to_json(&headers.host, &emergency_access.grantor_uuid, Some(&cipher_sync_data), &mut conn).await);
|
|
|
}
|
|
|
|
|
|
Ok(Json(json!({
|
|
@@ -633,7 +627,7 @@ async fn takeover_emergency_access(emer_id: String, headers: Headers, mut conn:
|
|
|
})))
|
|
|
}
|
|
|
|
|
|
-#[derive(Deserialize, Debug)]
|
|
|
+#[derive(Deserialize)]
|
|
|
#[allow(non_snake_case)]
|
|
|
struct EmergencyAccessPasswordData {
|
|
|
NewMasterPasswordHash: String,
|
|
@@ -738,40 +732,44 @@ pub async fn emergency_request_timeout_job(pool: DbPool) {
|
|
|
}
|
|
|
|
|
|
if let Ok(mut conn) = pool.get().await {
|
|
|
- let emergency_access_list = EmergencyAccess::find_all_recoveries(&mut conn).await;
|
|
|
+ let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&mut conn).await;
|
|
|
|
|
|
if emergency_access_list.is_empty() {
|
|
|
debug!("No emergency request timeout to approve");
|
|
|
}
|
|
|
|
|
|
+ let now = Utc::now().naive_utc();
|
|
|
for mut emer in emergency_access_list {
|
|
|
- if emer.recovery_initiated_at.is_some()
|
|
|
- && Utc::now().naive_utc()
|
|
|
- >= emer.recovery_initiated_at.unwrap() + Duration::days(i64::from(emer.wait_time_days))
|
|
|
- {
|
|
|
- emer.status = EmergencyAccessStatus::RecoveryApproved as i32;
|
|
|
- emer.save(&mut conn).await.expect("Cannot save emergency access on job");
|
|
|
+ // The find_all_recoveries_initiated already checks if the recovery_initiated_at is not null (None)
|
|
|
+ let recovery_allowed_at =
|
|
|
+ emer.recovery_initiated_at.unwrap() + Duration::days(i64::from(emer.wait_time_days));
|
|
|
+ if recovery_allowed_at.le(&now) {
|
|
|
+ // Only update the access status
|
|
|
+ // Updating the whole record could cause issues when the emergency_notification_reminder_job is also active
|
|
|
+ emer.update_access_status_and_save(EmergencyAccessStatus::RecoveryApproved as i32, &now, &mut conn)
|
|
|
+ .await
|
|
|
+ .expect("Unable to update emergency access status");
|
|
|
|
|
|
if CONFIG.mail_enabled() {
|
|
|
// get grantor user to send Accepted email
|
|
|
let grantor_user =
|
|
|
- User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found.");
|
|
|
+ User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found");
|
|
|
|
|
|
// get grantee user to send Accepted email
|
|
|
let grantee_user =
|
|
|
- User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &mut conn)
|
|
|
+ User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &mut conn)
|
|
|
.await
|
|
|
- .expect("Grantee user not found.");
|
|
|
+ .expect("Grantee user not found");
|
|
|
|
|
|
mail::send_emergency_access_recovery_timed_out(
|
|
|
&grantor_user.email,
|
|
|
- &grantee_user.name.clone(),
|
|
|
+ &grantee_user.name,
|
|
|
emer.get_type_as_str(),
|
|
|
)
|
|
|
.await
|
|
|
.expect("Error on sending email");
|
|
|
|
|
|
- mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name.clone())
|
|
|
+ mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name)
|
|
|
.await
|
|
|
.expect("Error on sending email");
|
|
|
}
|
|
@@ -789,38 +787,47 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) {
|
|
|
}
|
|
|
|
|
|
if let Ok(mut conn) = pool.get().await {
|
|
|
- let emergency_access_list = EmergencyAccess::find_all_recoveries(&mut conn).await;
|
|
|
+ let emergency_access_list = EmergencyAccess::find_all_recoveries_initiated(&mut conn).await;
|
|
|
|
|
|
if emergency_access_list.is_empty() {
|
|
|
debug!("No emergency request reminder notification to send");
|
|
|
}
|
|
|
|
|
|
+ let now = Utc::now().naive_utc();
|
|
|
for mut emer in emergency_access_list {
|
|
|
- if (emer.recovery_initiated_at.is_some()
|
|
|
- && Utc::now().naive_utc()
|
|
|
- >= emer.recovery_initiated_at.unwrap() + Duration::days((i64::from(emer.wait_time_days)) - 1))
|
|
|
- && (emer.last_notification_at.is_none()
|
|
|
- || (emer.last_notification_at.is_some()
|
|
|
- && Utc::now().naive_utc() >= emer.last_notification_at.unwrap() + Duration::days(1)))
|
|
|
- {
|
|
|
- emer.save(&mut conn).await.expect("Cannot save emergency access on job");
|
|
|
+ // The find_all_recoveries_initiated already checks if the recovery_initiated_at is not null (None)
|
|
|
+ // Calculate the day before the recovery will become active
|
|
|
+ let final_recovery_reminder_at =
|
|
|
+ emer.recovery_initiated_at.unwrap() + Duration::days(i64::from(emer.wait_time_days - 1));
|
|
|
+ // Calculate if a day has passed since the previous notification, else no notification has been sent before
|
|
|
+ let next_recovery_reminder_at = if let Some(last_notification_at) = emer.last_notification_at {
|
|
|
+ last_notification_at + Duration::days(1)
|
|
|
+ } else {
|
|
|
+ now
|
|
|
+ };
|
|
|
+ if final_recovery_reminder_at.le(&now) && next_recovery_reminder_at.le(&now) {
|
|
|
+ // Only update the last notification date
|
|
|
+ // Updating the whole record could cause issues when the emergency_request_timeout_job is also active
|
|
|
+ emer.update_last_notification_date_and_save(&now, &mut conn)
|
|
|
+ .await
|
|
|
+ .expect("Unable to update emergency access notification date");
|
|
|
|
|
|
if CONFIG.mail_enabled() {
|
|
|
// get grantor user to send Accepted email
|
|
|
let grantor_user =
|
|
|
- User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found.");
|
|
|
+ User::find_by_uuid(&emer.grantor_uuid, &mut conn).await.expect("Grantor user not found");
|
|
|
|
|
|
// get grantee user to send Accepted email
|
|
|
let grantee_user =
|
|
|
- User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &mut conn)
|
|
|
+ User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid"), &mut conn)
|
|
|
.await
|
|
|
- .expect("Grantee user not found.");
|
|
|
+ .expect("Grantee user not found");
|
|
|
|
|
|
mail::send_emergency_access_recovery_reminder(
|
|
|
&grantor_user.email,
|
|
|
- &grantee_user.name.clone(),
|
|
|
+ &grantee_user.name,
|
|
|
emer.get_type_as_str(),
|
|
|
- &emer.wait_time_days.to_string(), // TODO(jjlin): This should be the number of days left.
|
|
|
+ "1", // This notification is only triggered one day before the activation
|
|
|
)
|
|
|
.await
|
|
|
.expect("Error on sending email");
|