1 use chrono::{Duration, Utc};
2 use rocket::Route;
3 use rocket_contrib::json::Json;
4 use serde_json::Value;
5 use std::borrow::Borrow;
6 
7 use crate::{
8     api::{EmptyResult, JsonResult, JsonUpcase, NumberOrString},
9     auth::{decode_emergency_access_invite, Headers},
10     db::{models::*, DbConn, DbPool},
11     mail, CONFIG,
12 };
13 
routes() -> Vec<Route>14 pub fn routes() -> Vec<Route> {
15     routes![
16         get_contacts,
17         get_grantees,
18         get_emergency_access,
19         put_emergency_access,
20         delete_emergency_access,
21         post_delete_emergency_access,
22         send_invite,
23         resend_invite,
24         accept_invite,
25         confirm_emergency_access,
26         initiate_emergency_access,
27         approve_emergency_access,
28         reject_emergency_access,
29         takeover_emergency_access,
30         password_emergency_access,
31         view_emergency_access,
32         policies_emergency_access,
33     ]
34 }
35 
36 // region get
37 
38 #[get("/emergency-access/trusted")]
get_contacts(headers: Headers, conn: DbConn) -> JsonResult39 fn get_contacts(headers: Headers, conn: DbConn) -> JsonResult {
40     check_emergency_access_allowed()?;
41 
42     let emergency_access_list = EmergencyAccess::find_all_by_grantor_uuid(&headers.user.uuid, &conn);
43 
44     let emergency_access_list_json: Vec<Value> =
45         emergency_access_list.iter().map(|e| e.to_json_grantee_details(&conn)).collect();
46 
47     Ok(Json(json!({
48       "Data": emergency_access_list_json,
49       "Object": "list",
50       "ContinuationToken": null
51     })))
52 }
53 
54 #[get("/emergency-access/granted")]
get_grantees(headers: Headers, conn: DbConn) -> JsonResult55 fn get_grantees(headers: Headers, conn: DbConn) -> JsonResult {
56     check_emergency_access_allowed()?;
57 
58     let emergency_access_list = EmergencyAccess::find_all_by_grantee_uuid(&headers.user.uuid, &conn);
59 
60     let emergency_access_list_json: Vec<Value> =
61         emergency_access_list.iter().map(|e| e.to_json_grantor_details(&conn)).collect();
62 
63     Ok(Json(json!({
64       "Data": emergency_access_list_json,
65       "Object": "list",
66       "ContinuationToken": null
67     })))
68 }
69 
70 #[get("/emergency-access/<emer_id>")]
get_emergency_access(emer_id: String, conn: DbConn) -> JsonResult71 fn get_emergency_access(emer_id: String, conn: DbConn) -> JsonResult {
72     check_emergency_access_allowed()?;
73 
74     match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
75         Some(emergency_access) => Ok(Json(emergency_access.to_json_grantee_details(&conn))),
76         None => err!("Emergency access not valid."),
77     }
78 }
79 
80 // endregion
81 
82 // region put/post
83 
84 #[derive(Deserialize, Debug)]
85 #[allow(non_snake_case)]
86 struct EmergencyAccessUpdateData {
87     Type: NumberOrString,
88     WaitTimeDays: i32,
89     KeyEncrypted: Option<String>,
90 }
91 
92 #[put("/emergency-access/<emer_id>", data = "<data>")]
put_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult93 fn put_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult {
94     post_emergency_access(emer_id, data, conn)
95 }
96 
97 #[post("/emergency-access/<emer_id>", data = "<data>")]
post_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult98 fn post_emergency_access(emer_id: String, data: JsonUpcase<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult {
99     check_emergency_access_allowed()?;
100 
101     let data: EmergencyAccessUpdateData = data.into_inner().data;
102 
103     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
104         Some(emergency_access) => emergency_access,
105         None => err!("Emergency access not valid."),
106     };
107 
108     let new_type = match EmergencyAccessType::from_str(&data.Type.into_string()) {
109         Some(new_type) => new_type as i32,
110         None => err!("Invalid emergency access type."),
111     };
112 
113     emergency_access.atype = new_type;
114     emergency_access.wait_time_days = data.WaitTimeDays;
115     emergency_access.key_encrypted = data.KeyEncrypted;
116 
117     emergency_access.save(&conn)?;
118     Ok(Json(emergency_access.to_json()))
119 }
120 
121 // endregion
122 
123 // region delete
124 
125 #[delete("/emergency-access/<emer_id>")]
delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult126 fn delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
127     check_emergency_access_allowed()?;
128 
129     let grantor_user = headers.user;
130 
131     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
132         Some(emer) => {
133             if emer.grantor_uuid != grantor_user.uuid && emer.grantee_uuid != Some(grantor_user.uuid) {
134                 err!("Emergency access not valid.")
135             }
136             emer
137         }
138         None => err!("Emergency access not valid."),
139     };
140     emergency_access.delete(&conn)?;
141     Ok(())
142 }
143 
144 #[post("/emergency-access/<emer_id>/delete")]
post_delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult145 fn post_delete_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
146     delete_emergency_access(emer_id, headers, conn)
147 }
148 
149 // endregion
150 
151 // region invite
152 
153 #[derive(Deserialize, Debug)]
154 #[allow(non_snake_case)]
155 struct EmergencyAccessInviteData {
156     Email: String,
157     Type: NumberOrString,
158     WaitTimeDays: i32,
159 }
160 
161 #[post("/emergency-access/invite", data = "<data>")]
send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, conn: DbConn) -> EmptyResult162 fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Headers, conn: DbConn) -> EmptyResult {
163     check_emergency_access_allowed()?;
164 
165     let data: EmergencyAccessInviteData = data.into_inner().data;
166     let email = data.Email.to_lowercase();
167     let wait_time_days = data.WaitTimeDays;
168 
169     let emergency_access_status = EmergencyAccessStatus::Invited as i32;
170 
171     let new_type = match EmergencyAccessType::from_str(&data.Type.into_string()) {
172         Some(new_type) => new_type as i32,
173         None => err!("Invalid emergency access type."),
174     };
175 
176     let grantor_user = headers.user;
177 
178     // avoid setting yourself as emergency contact
179     if email == grantor_user.email {
180         err!("You can not set yourself as an emergency contact.")
181     }
182 
183     let grantee_user = match User::find_by_mail(&email, &conn) {
184         None => {
185             if !CONFIG.signups_allowed() {
186                 err!(format!("Grantee user does not exist: {}", email))
187             }
188 
189             if !CONFIG.is_email_domain_allowed(&email) {
190                 err!("Email domain not eligible for invitations")
191             }
192 
193             if !CONFIG.mail_enabled() {
194                 let invitation = Invitation::new(email.clone());
195                 invitation.save(&conn)?;
196             }
197 
198             let mut user = User::new(email.clone());
199             user.save(&conn)?;
200             user
201         }
202         Some(user) => user,
203     };
204 
205     if EmergencyAccess::find_by_grantor_uuid_and_grantee_uuid_or_email(
206         &grantor_user.uuid,
207         &grantee_user.uuid,
208         &grantee_user.email,
209         &conn,
210     )
211     .is_some()
212     {
213         err!(format!("Grantee user already invited: {}", email))
214     }
215 
216     let mut new_emergency_access = EmergencyAccess::new(
217         grantor_user.uuid.clone(),
218         Some(grantee_user.email.clone()),
219         emergency_access_status,
220         new_type,
221         wait_time_days,
222     );
223     new_emergency_access.save(&conn)?;
224 
225     if CONFIG.mail_enabled() {
226         mail::send_emergency_access_invite(
227             &grantee_user.email,
228             &grantee_user.uuid,
229             Some(new_emergency_access.uuid),
230             Some(grantor_user.name.clone()),
231             Some(grantor_user.email),
232         )?;
233     } else {
234         // Automatically mark user as accepted if no email invites
235         match User::find_by_mail(&email, &conn) {
236             Some(user) => {
237                 match accept_invite_process(user.uuid, new_emergency_access.uuid, Some(email), conn.borrow()) {
238                     Ok(v) => (v),
239                     Err(e) => err!(e.to_string()),
240                 }
241             }
242             None => err!("Grantee user not found."),
243         }
244     }
245 
246     Ok(())
247 }
248 
249 #[post("/emergency-access/<emer_id>/reinvite")]
resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult250 fn resend_invite(emer_id: String, headers: Headers, conn: DbConn) -> EmptyResult {
251     check_emergency_access_allowed()?;
252 
253     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
254         Some(emer) => emer,
255         None => err!("Emergency access not valid."),
256     };
257 
258     if emergency_access.grantor_uuid != headers.user.uuid {
259         err!("Emergency access not valid.");
260     }
261 
262     if emergency_access.status != EmergencyAccessStatus::Invited as i32 {
263         err!("The grantee user is already accepted or confirmed to the organization");
264     }
265 
266     let email = match emergency_access.email.clone() {
267         Some(email) => email,
268         None => err!("Email not valid."),
269     };
270 
271     let grantee_user = match User::find_by_mail(&email, &conn) {
272         Some(user) => user,
273         None => err!("Grantee user not found."),
274     };
275 
276     let grantor_user = headers.user;
277 
278     if CONFIG.mail_enabled() {
279         mail::send_emergency_access_invite(
280             &email,
281             &grantor_user.uuid,
282             Some(emergency_access.uuid),
283             Some(grantor_user.name.clone()),
284             Some(grantor_user.email),
285         )?;
286     } else {
287         if Invitation::find_by_mail(&email, &conn).is_none() {
288             let invitation = Invitation::new(email);
289             invitation.save(&conn)?;
290         }
291 
292         // Automatically mark user as accepted if no email invites
293         match accept_invite_process(grantee_user.uuid, emergency_access.uuid, emergency_access.email, conn.borrow()) {
294             Ok(v) => (v),
295             Err(e) => err!(e.to_string()),
296         }
297     }
298 
299     Ok(())
300 }
301 
302 #[derive(Deserialize)]
303 #[allow(non_snake_case)]
304 struct AcceptData {
305     Token: String,
306 }
307 
308 #[post("/emergency-access/<emer_id>/accept", data = "<data>")]
accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult309 fn accept_invite(emer_id: String, data: JsonUpcase<AcceptData>, conn: DbConn) -> EmptyResult {
310     check_emergency_access_allowed()?;
311 
312     let data: AcceptData = data.into_inner().data;
313     let token = &data.Token;
314     let claims = decode_emergency_access_invite(token)?;
315 
316     let grantee_user = match User::find_by_mail(&claims.email, &conn) {
317         Some(user) => {
318             Invitation::take(&claims.email, &conn);
319             user
320         }
321         None => err!("Invited user not found"),
322     };
323 
324     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
325         Some(emer) => emer,
326         None => err!("Emergency access not valid."),
327     };
328 
329     // get grantor user to send Accepted email
330     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
331         Some(user) => user,
332         None => err!("Grantor user not found."),
333     };
334 
335     if (claims.emer_id.is_some() && emer_id == claims.emer_id.unwrap())
336         && (claims.grantor_name.is_some() && grantor_user.name == claims.grantor_name.unwrap())
337         && (claims.grantor_email.is_some() && grantor_user.email == claims.grantor_email.unwrap())
338     {
339         match accept_invite_process(grantee_user.uuid.clone(), emer_id, Some(grantee_user.email.clone()), &conn) {
340             Ok(v) => (v),
341             Err(e) => err!(e.to_string()),
342         }
343 
344         if CONFIG.mail_enabled() {
345             mail::send_emergency_access_invite_accepted(&grantor_user.email, &grantee_user.email)?;
346         }
347 
348         Ok(())
349     } else {
350         err!("Emergency access invitation error.")
351     }
352 }
353 
accept_invite_process(grantee_uuid: String, emer_id: String, email: Option<String>, conn: &DbConn) -> EmptyResult354 fn accept_invite_process(grantee_uuid: String, emer_id: String, email: Option<String>, conn: &DbConn) -> EmptyResult {
355     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, conn) {
356         Some(emer) => emer,
357         None => err!("Emergency access not valid."),
358     };
359 
360     let emer_email = emergency_access.email;
361     if emer_email.is_none() || emer_email != email {
362         err!("User email does not match invite.");
363     }
364 
365     if emergency_access.status == EmergencyAccessStatus::Accepted as i32 {
366         err!("Emergency contact already accepted.");
367     }
368 
369     emergency_access.status = EmergencyAccessStatus::Accepted as i32;
370     emergency_access.grantee_uuid = Some(grantee_uuid);
371     emergency_access.email = None;
372     emergency_access.save(conn)
373 }
374 
375 #[derive(Deserialize)]
376 #[allow(non_snake_case)]
377 struct ConfirmData {
378     Key: String,
379 }
380 
381 #[post("/emergency-access/<emer_id>/confirm", data = "<data>")]
confirm_emergency_access( emer_id: String, data: JsonUpcase<ConfirmData>, headers: Headers, conn: DbConn, ) -> JsonResult382 fn confirm_emergency_access(
383     emer_id: String,
384     data: JsonUpcase<ConfirmData>,
385     headers: Headers,
386     conn: DbConn,
387 ) -> JsonResult {
388     check_emergency_access_allowed()?;
389 
390     let confirming_user = headers.user;
391     let data: ConfirmData = data.into_inner().data;
392     let key = data.Key;
393 
394     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
395         Some(emer) => emer,
396         None => err!("Emergency access not valid."),
397     };
398 
399     if emergency_access.status != EmergencyAccessStatus::Accepted as i32
400         || emergency_access.grantor_uuid != confirming_user.uuid
401     {
402         err!("Emergency access not valid.")
403     }
404 
405     let grantor_user = match User::find_by_uuid(&confirming_user.uuid, &conn) {
406         Some(user) => user,
407         None => err!("Grantor user not found."),
408     };
409 
410     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
411         let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
412             Some(user) => user,
413             None => err!("Grantee user not found."),
414         };
415 
416         emergency_access.status = EmergencyAccessStatus::Confirmed as i32;
417         emergency_access.key_encrypted = Some(key);
418         emergency_access.email = None;
419 
420         emergency_access.save(&conn)?;
421 
422         if CONFIG.mail_enabled() {
423             mail::send_emergency_access_invite_confirmed(&grantee_user.email, &grantor_user.name)?;
424         }
425         Ok(Json(emergency_access.to_json()))
426     } else {
427         err!("Grantee user not found.")
428     }
429 }
430 
431 // endregion
432 
433 // region access emergency access
434 
435 #[post("/emergency-access/<emer_id>/initiate")]
initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult436 fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
437     check_emergency_access_allowed()?;
438 
439     let initiating_user = headers.user;
440     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
441         Some(emer) => emer,
442         None => err!("Emergency access not valid."),
443     };
444 
445     if emergency_access.status != EmergencyAccessStatus::Confirmed as i32
446         || emergency_access.grantee_uuid != Some(initiating_user.uuid.clone())
447     {
448         err!("Emergency access not valid.")
449     }
450 
451     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
452         Some(user) => user,
453         None => err!("Grantor user not found."),
454     };
455 
456     let now = Utc::now().naive_utc();
457     emergency_access.status = EmergencyAccessStatus::RecoveryInitiated as i32;
458     emergency_access.updated_at = now;
459     emergency_access.recovery_initiated_at = Some(now);
460     emergency_access.last_notification_at = Some(now);
461     emergency_access.save(&conn)?;
462 
463     if CONFIG.mail_enabled() {
464         mail::send_emergency_access_recovery_initiated(
465             &grantor_user.email,
466             &initiating_user.name,
467             emergency_access.get_type_as_str(),
468             &emergency_access.wait_time_days.clone().to_string(),
469         )?;
470     }
471     Ok(Json(emergency_access.to_json()))
472 }
473 
474 #[post("/emergency-access/<emer_id>/approve")]
approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult475 fn approve_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
476     check_emergency_access_allowed()?;
477 
478     let approving_user = headers.user;
479     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
480         Some(emer) => emer,
481         None => err!("Emergency access not valid."),
482     };
483 
484     if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
485         || emergency_access.grantor_uuid != approving_user.uuid
486     {
487         err!("Emergency access not valid.")
488     }
489 
490     let grantor_user = match User::find_by_uuid(&approving_user.uuid, &conn) {
491         Some(user) => user,
492         None => err!("Grantor user not found."),
493     };
494 
495     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
496         let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
497             Some(user) => user,
498             None => err!("Grantee user not found."),
499         };
500 
501         emergency_access.status = EmergencyAccessStatus::RecoveryApproved as i32;
502         emergency_access.save(&conn)?;
503 
504         if CONFIG.mail_enabled() {
505             mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name)?;
506         }
507         Ok(Json(emergency_access.to_json()))
508     } else {
509         err!("Grantee user not found.")
510     }
511 }
512 
513 #[post("/emergency-access/<emer_id>/reject")]
reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult514 fn reject_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
515     check_emergency_access_allowed()?;
516 
517     let rejecting_user = headers.user;
518     let mut emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
519         Some(emer) => emer,
520         None => err!("Emergency access not valid."),
521     };
522 
523     if (emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
524         && emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32)
525         || emergency_access.grantor_uuid != rejecting_user.uuid
526     {
527         err!("Emergency access not valid.")
528     }
529 
530     let grantor_user = match User::find_by_uuid(&rejecting_user.uuid, &conn) {
531         Some(user) => user,
532         None => err!("Grantor user not found."),
533     };
534 
535     if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
536         let grantee_user = match User::find_by_uuid(grantee_uuid, &conn) {
537             Some(user) => user,
538             None => err!("Grantee user not found."),
539         };
540 
541         emergency_access.status = EmergencyAccessStatus::Confirmed as i32;
542         emergency_access.save(&conn)?;
543 
544         if CONFIG.mail_enabled() {
545             mail::send_emergency_access_recovery_rejected(&grantee_user.email, &grantor_user.name)?;
546         }
547         Ok(Json(emergency_access.to_json()))
548     } else {
549         err!("Grantee user not found.")
550     }
551 }
552 
553 // endregion
554 
555 // region action
556 
557 #[post("/emergency-access/<emer_id>/view")]
view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult558 fn view_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
559     check_emergency_access_allowed()?;
560 
561     let requesting_user = headers.user;
562     let host = headers.host;
563     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
564         Some(emer) => emer,
565         None => err!("Emergency access not valid."),
566     };
567 
568     if !is_valid_request(&emergency_access, requesting_user.uuid, EmergencyAccessType::View) {
569         err!("Emergency access not valid.")
570     }
571 
572     let ciphers = Cipher::find_owned_by_user(&emergency_access.grantor_uuid, &conn);
573 
574     let ciphers_json: Vec<Value> =
575         ciphers.iter().map(|c| c.to_json(&host, &emergency_access.grantor_uuid, &conn)).collect();
576 
577     Ok(Json(json!({
578       "Ciphers": ciphers_json,
579       "KeyEncrypted": &emergency_access.key_encrypted,
580       "Object": "emergencyAccessView",
581     })))
582 }
583 
584 #[post("/emergency-access/<emer_id>/takeover")]
takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult585 fn takeover_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
586     check_emergency_access_allowed()?;
587 
588     let requesting_user = headers.user;
589     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
590         Some(emer) => emer,
591         None => err!("Emergency access not valid."),
592     };
593 
594     if !is_valid_request(&emergency_access, requesting_user.uuid, EmergencyAccessType::Takeover) {
595         err!("Emergency access not valid.")
596     }
597 
598     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
599         Some(user) => user,
600         None => err!("Grantor user not found."),
601     };
602 
603     Ok(Json(json!({
604       "Kdf": grantor_user.client_kdf_type,
605       "KdfIterations": grantor_user.client_kdf_iter,
606       "KeyEncrypted": &emergency_access.key_encrypted,
607       "Object": "emergencyAccessTakeover",
608     })))
609 }
610 
611 #[derive(Deserialize, Debug)]
612 #[allow(non_snake_case)]
613 struct EmergencyAccessPasswordData {
614     NewMasterPasswordHash: String,
615     Key: String,
616 }
617 
618 #[post("/emergency-access/<emer_id>/password", data = "<data>")]
password_emergency_access( emer_id: String, data: JsonUpcase<EmergencyAccessPasswordData>, headers: Headers, conn: DbConn, ) -> EmptyResult619 fn password_emergency_access(
620     emer_id: String,
621     data: JsonUpcase<EmergencyAccessPasswordData>,
622     headers: Headers,
623     conn: DbConn,
624 ) -> EmptyResult {
625     check_emergency_access_allowed()?;
626 
627     let data: EmergencyAccessPasswordData = data.into_inner().data;
628     let new_master_password_hash = &data.NewMasterPasswordHash;
629     let key = data.Key;
630 
631     let requesting_user = headers.user;
632     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
633         Some(emer) => emer,
634         None => err!("Emergency access not valid."),
635     };
636 
637     if !is_valid_request(&emergency_access, requesting_user.uuid, EmergencyAccessType::Takeover) {
638         err!("Emergency access not valid.")
639     }
640 
641     let mut grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
642         Some(user) => user,
643         None => err!("Grantor user not found."),
644     };
645 
646     // change grantor_user password
647     grantor_user.set_password(new_master_password_hash, None);
648     grantor_user.akey = key;
649     grantor_user.save(&conn)?;
650 
651     // Disable TwoFactor providers since they will otherwise block logins
652     TwoFactor::delete_all_by_user(&grantor_user.uuid, &conn)?;
653 
654     // Removing owner, check that there are at least another owner
655     let user_org_grantor = UserOrganization::find_any_state_by_user(&grantor_user.uuid, &conn);
656 
657     // Remove grantor from all organisations unless Owner
658     for user_org in user_org_grantor {
659         if user_org.atype != UserOrgType::Owner as i32 {
660             user_org.delete(&conn)?;
661         }
662     }
663     Ok(())
664 }
665 
666 // endregion
667 
668 #[get("/emergency-access/<emer_id>/policies")]
policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult669 fn policies_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> JsonResult {
670     let requesting_user = headers.user;
671     let emergency_access = match EmergencyAccess::find_by_uuid(&emer_id, &conn) {
672         Some(emer) => emer,
673         None => err!("Emergency access not valid."),
674     };
675 
676     if !is_valid_request(&emergency_access, requesting_user.uuid, EmergencyAccessType::Takeover) {
677         err!("Emergency access not valid.")
678     }
679 
680     let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &conn) {
681         Some(user) => user,
682         None => err!("Grantor user not found."),
683     };
684 
685     let policies = OrgPolicy::find_confirmed_by_user(&grantor_user.uuid, &conn);
686     let policies_json: Vec<Value> = policies.iter().map(OrgPolicy::to_json).collect();
687 
688     Ok(Json(json!({
689         "Data": policies_json,
690         "Object": "list",
691         "ContinuationToken": null
692     })))
693 }
694 
is_valid_request( emergency_access: &EmergencyAccess, requesting_user_uuid: String, requested_access_type: EmergencyAccessType, ) -> bool695 fn is_valid_request(
696     emergency_access: &EmergencyAccess,
697     requesting_user_uuid: String,
698     requested_access_type: EmergencyAccessType,
699 ) -> bool {
700     emergency_access.grantee_uuid == Some(requesting_user_uuid)
701         && emergency_access.status == EmergencyAccessStatus::RecoveryApproved as i32
702         && emergency_access.atype == requested_access_type as i32
703 }
704 
check_emergency_access_allowed() -> EmptyResult705 fn check_emergency_access_allowed() -> EmptyResult {
706     if !CONFIG.emergency_access_allowed() {
707         err!("Emergency access is not allowed.")
708     }
709     Ok(())
710 }
711 
emergency_request_timeout_job(pool: DbPool)712 pub fn emergency_request_timeout_job(pool: DbPool) {
713     debug!("Start emergency_request_timeout_job");
714     if !CONFIG.emergency_access_allowed() {
715         return;
716     }
717 
718     if let Ok(conn) = pool.get() {
719         let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn);
720 
721         if emergency_access_list.is_empty() {
722             debug!("No emergency request timeout to approve");
723         }
724 
725         for mut emer in emergency_access_list {
726             if emer.recovery_initiated_at.is_some()
727                 && Utc::now().naive_utc()
728                     >= emer.recovery_initiated_at.unwrap() + Duration::days(emer.wait_time_days as i64)
729             {
730                 emer.status = EmergencyAccessStatus::RecoveryApproved as i32;
731                 emer.save(&conn).expect("Cannot save emergency access on job");
732 
733                 if CONFIG.mail_enabled() {
734                     // get grantor user to send Accepted email
735                     let grantor_user = User::find_by_uuid(&emer.grantor_uuid, &conn).expect("Grantor user not found.");
736 
737                     // get grantee user to send Accepted email
738                     let grantee_user =
739                         User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &conn)
740                             .expect("Grantee user not found.");
741 
742                     mail::send_emergency_access_recovery_timed_out(
743                         &grantor_user.email,
744                         &grantee_user.name.clone(),
745                         emer.get_type_as_str(),
746                     )
747                     .expect("Error on sending email");
748 
749                     mail::send_emergency_access_recovery_approved(&grantee_user.email, &grantor_user.name.clone())
750                         .expect("Error on sending email");
751                 }
752             }
753         }
754     } else {
755         error!("Failed to get DB connection while searching emergency request timed out")
756     }
757 }
758 
emergency_notification_reminder_job(pool: DbPool)759 pub fn emergency_notification_reminder_job(pool: DbPool) {
760     debug!("Start emergency_notification_reminder_job");
761     if !CONFIG.emergency_access_allowed() {
762         return;
763     }
764 
765     if let Ok(conn) = pool.get() {
766         let emergency_access_list = EmergencyAccess::find_all_recoveries(&conn);
767 
768         if emergency_access_list.is_empty() {
769             debug!("No emergency request reminder notification to send");
770         }
771 
772         for mut emer in emergency_access_list {
773             if (emer.recovery_initiated_at.is_some()
774                 && Utc::now().naive_utc()
775                     >= emer.recovery_initiated_at.unwrap() + Duration::days((emer.wait_time_days as i64) - 1))
776                 && (emer.last_notification_at.is_none()
777                     || (emer.last_notification_at.is_some()
778                         && Utc::now().naive_utc() >= emer.last_notification_at.unwrap() + Duration::days(1)))
779             {
780                 emer.save(&conn).expect("Cannot save emergency access on job");
781 
782                 if CONFIG.mail_enabled() {
783                     // get grantor user to send Accepted email
784                     let grantor_user = User::find_by_uuid(&emer.grantor_uuid, &conn).expect("Grantor user not found.");
785 
786                     // get grantee user to send Accepted email
787                     let grantee_user =
788                         User::find_by_uuid(&emer.grantee_uuid.clone().expect("Grantee user invalid."), &conn)
789                             .expect("Grantee user not found.");
790 
791                     mail::send_emergency_access_recovery_reminder(
792                         &grantor_user.email,
793                         &grantee_user.name.clone(),
794                         emer.get_type_as_str(),
795                         &emer.wait_time_days.to_string(), // TODO(jjlin): This should be the number of days left.
796                     )
797                     .expect("Error on sending email");
798                 }
799             }
800         }
801     } else {
802         error!("Failed to get DB connection while searching emergency notification reminder")
803     }
804 }
805