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