Skip to content

Commit d04b94b

Browse files
authored
Some fixes for emergency access (#4715)
- Add missing `Headers` parameter for some functions This allowed any request from allowing these endpoints by not validating the user correctly. - Changed the functions to retreive the emergency access record by using the user uuid which calls the endpoint, instead of validating afterwards. This is more secure and prevents the need of an if check.
1 parent 247d070 commit d04b94b

File tree

3 files changed

+116
-95
lines changed

3 files changed

+116
-95
lines changed

src/api/core/accounts.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -515,15 +515,12 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
515515

516516
// Update emergency access data
517517
for emergency_access_data in data.emergency_access_keys {
518-
let mut saved_emergency_access = match EmergencyAccess::find_by_uuid(&emergency_access_data.id, &mut conn).await
519-
{
520-
Some(emergency_access) => emergency_access,
521-
None => err!("Emergency access doesn't exist"),
522-
};
523-
524-
if &saved_emergency_access.grantor_uuid != user_uuid {
525-
err!("The emergency access is not owned by the user")
526-
}
518+
let mut saved_emergency_access =
519+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(&emergency_access_data.id, user_uuid, &mut conn).await
520+
{
521+
Some(emergency_access) => emergency_access,
522+
None => err!("Emergency access doesn't exist or is not owned by the user"),
523+
};
527524

528525
saved_emergency_access.key_encrypted = Some(emergency_access_data.key_encrypted);
529526
saved_emergency_access.save(&mut conn).await?

src/api/core/emergency_access.rs

+90-77
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ async fn get_grantees(headers: Headers, mut conn: DbConn) -> Json<Value> {
9393
}
9494

9595
#[get("/emergency-access/<emer_id>")]
96-
async fn get_emergency_access(emer_id: &str, mut conn: DbConn) -> JsonResult {
96+
async fn get_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
9797
check_emergency_access_enabled()?;
9898

99-
match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
99+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await {
100100
Some(emergency_access) => Ok(Json(
101101
emergency_access.to_json_grantee_details(&mut conn).await.expect("Grantee user should exist but does not!"),
102102
)),
@@ -117,20 +117,31 @@ struct EmergencyAccessUpdateData {
117117
}
118118

119119
#[put("/emergency-access/<emer_id>", data = "<data>")]
120-
async fn put_emergency_access(emer_id: &str, data: Json<EmergencyAccessUpdateData>, conn: DbConn) -> JsonResult {
121-
post_emergency_access(emer_id, data, conn).await
120+
async fn put_emergency_access(
121+
emer_id: &str,
122+
data: Json<EmergencyAccessUpdateData>,
123+
headers: Headers,
124+
conn: DbConn,
125+
) -> JsonResult {
126+
post_emergency_access(emer_id, data, headers, conn).await
122127
}
123128

124129
#[post("/emergency-access/<emer_id>", data = "<data>")]
125-
async fn post_emergency_access(emer_id: &str, data: Json<EmergencyAccessUpdateData>, mut conn: DbConn) -> JsonResult {
130+
async fn post_emergency_access(
131+
emer_id: &str,
132+
data: Json<EmergencyAccessUpdateData>,
133+
headers: Headers,
134+
mut conn: DbConn,
135+
) -> JsonResult {
126136
check_emergency_access_enabled()?;
127137

128138
let data: EmergencyAccessUpdateData = data.into_inner();
129139

130-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
131-
Some(emergency_access) => emergency_access,
132-
None => err!("Emergency access not valid."),
133-
};
140+
let mut emergency_access =
141+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await {
142+
Some(emergency_access) => emergency_access,
143+
None => err!("Emergency access not valid."),
144+
};
134145

135146
let new_type = match EmergencyAccessType::from_str(&data.r#type.into_string()) {
136147
Some(new_type) => new_type as i32,
@@ -155,17 +166,21 @@ async fn post_emergency_access(emer_id: &str, data: Json<EmergencyAccessUpdateDa
155166
async fn delete_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult {
156167
check_emergency_access_enabled()?;
157168

158-
let grantor_user = headers.user;
159-
160-
let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
161-
Some(emer) => {
162-
if emer.grantor_uuid != grantor_user.uuid && emer.grantee_uuid != Some(grantor_user.uuid) {
163-
err!("Emergency access not valid.")
164-
}
165-
emer
169+
let emergency_access = match (
170+
EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await,
171+
EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &headers.user.uuid, &mut conn).await,
172+
) {
173+
(Some(grantor_emer), None) => {
174+
info!("Grantor deleted emergency access {emer_id}");
175+
grantor_emer
166176
}
167-
None => err!("Emergency access not valid."),
177+
(None, Some(grantee_emer)) => {
178+
info!("Grantee deleted emergency access {emer_id}");
179+
grantee_emer
180+
}
181+
_ => err!("Emergency access not valid."),
168182
};
183+
169184
emergency_access.delete(&mut conn).await?;
170185
Ok(())
171186
}
@@ -269,14 +284,11 @@ async fn send_invite(data: Json<EmergencyAccessInviteData>, headers: Headers, mu
269284
async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult {
270285
check_emergency_access_enabled()?;
271286

272-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
273-
Some(emer) => emer,
274-
None => err!("Emergency access not valid."),
275-
};
276-
277-
if emergency_access.grantor_uuid != headers.user.uuid {
278-
err!("Emergency access not valid.");
279-
}
287+
let mut emergency_access =
288+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await {
289+
Some(emer) => emer,
290+
None => err!("Emergency access not valid."),
291+
};
280292

281293
if emergency_access.status != EmergencyAccessStatus::Invited as i32 {
282294
err!("The grantee user is already accepted or confirmed to the organization");
@@ -342,10 +354,13 @@ async fn accept_invite(emer_id: &str, data: Json<AcceptData>, headers: Headers,
342354
None => err!("Invited user not found"),
343355
};
344356

345-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
346-
Some(emer) => emer,
347-
None => err!("Emergency access not valid."),
348-
};
357+
// We need to search for the uuid in combination with the email, since we do not yet store the uuid of the grantee in the database.
358+
// The uuid of the grantee gets stored once accepted.
359+
let mut emergency_access =
360+
match EmergencyAccess::find_by_uuid_and_grantee_email(emer_id, &headers.user.email, &mut conn).await {
361+
Some(emer) => emer,
362+
None => err!("Emergency access not valid."),
363+
};
349364

350365
// get grantor user to send Accepted email
351366
let grantor_user = match User::find_by_uuid(&emergency_access.grantor_uuid, &mut conn).await {
@@ -388,10 +403,11 @@ async fn confirm_emergency_access(
388403
let data: ConfirmData = data.into_inner();
389404
let key = data.key;
390405

391-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
392-
Some(emer) => emer,
393-
None => err!("Emergency access not valid."),
394-
};
406+
let mut emergency_access =
407+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &confirming_user.uuid, &mut conn).await {
408+
Some(emer) => emer,
409+
None => err!("Emergency access not valid."),
410+
};
395411

396412
if emergency_access.status != EmergencyAccessStatus::Accepted as i32
397413
|| emergency_access.grantor_uuid != confirming_user.uuid
@@ -434,14 +450,13 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
434450
check_emergency_access_enabled()?;
435451

436452
let initiating_user = headers.user;
437-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
438-
Some(emer) => emer,
439-
None => err!("Emergency access not valid."),
440-
};
453+
let mut emergency_access =
454+
match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &initiating_user.uuid, &mut conn).await {
455+
Some(emer) => emer,
456+
None => err!("Emergency access not valid."),
457+
};
441458

442-
if emergency_access.status != EmergencyAccessStatus::Confirmed as i32
443-
|| emergency_access.grantee_uuid != Some(initiating_user.uuid)
444-
{
459+
if emergency_access.status != EmergencyAccessStatus::Confirmed as i32 {
445460
err!("Emergency access not valid.")
446461
}
447462

@@ -473,14 +488,13 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
473488
async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
474489
check_emergency_access_enabled()?;
475490

476-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
477-
Some(emer) => emer,
478-
None => err!("Emergency access not valid."),
479-
};
491+
let mut emergency_access =
492+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await {
493+
Some(emer) => emer,
494+
None => err!("Emergency access not valid."),
495+
};
480496

481-
if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
482-
|| emergency_access.grantor_uuid != headers.user.uuid
483-
{
497+
if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32 {
484498
err!("Emergency access not valid.")
485499
}
486500

@@ -511,23 +525,18 @@ async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbC
511525
async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
512526
check_emergency_access_enabled()?;
513527

514-
let mut emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
515-
Some(emer) => emer,
516-
None => err!("Emergency access not valid."),
517-
};
528+
let mut emergency_access =
529+
match EmergencyAccess::find_by_uuid_and_grantor_uuid(emer_id, &headers.user.uuid, &mut conn).await {
530+
Some(emer) => emer,
531+
None => err!("Emergency access not valid."),
532+
};
518533

519-
if (emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
520-
&& emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32)
521-
|| emergency_access.grantor_uuid != headers.user.uuid
534+
if emergency_access.status != EmergencyAccessStatus::RecoveryInitiated as i32
535+
&& emergency_access.status != EmergencyAccessStatus::RecoveryApproved as i32
522536
{
523537
err!("Emergency access not valid.")
524538
}
525539

526-
let grantor_user = match User::find_by_uuid(&headers.user.uuid, &mut conn).await {
527-
Some(user) => user,
528-
None => err!("Grantor user not found."),
529-
};
530-
531540
if let Some(grantee_uuid) = emergency_access.grantee_uuid.as_ref() {
532541
let grantee_user = match User::find_by_uuid(grantee_uuid, &mut conn).await {
533542
Some(user) => user,
@@ -538,7 +547,7 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo
538547
emergency_access.save(&mut conn).await?;
539548

540549
if CONFIG.mail_enabled() {
541-
mail::send_emergency_access_recovery_rejected(&grantee_user.email, &grantor_user.name).await?;
550+
mail::send_emergency_access_recovery_rejected(&grantee_user.email, &headers.user.name).await?;
542551
}
543552
Ok(Json(emergency_access.to_json()))
544553
} else {
@@ -554,10 +563,11 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo
554563
async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
555564
check_emergency_access_enabled()?;
556565

557-
let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
558-
Some(emer) => emer,
559-
None => err!("Emergency access not valid."),
560-
};
566+
let emergency_access =
567+
match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &headers.user.uuid, &mut conn).await {
568+
Some(emer) => emer,
569+
None => err!("Emergency access not valid."),
570+
};
561571

562572
if !is_valid_request(&emergency_access, &headers.user.uuid, EmergencyAccessType::View) {
563573
err!("Emergency access not valid.")
@@ -592,10 +602,11 @@ async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
592602
check_emergency_access_enabled()?;
593603

594604
let requesting_user = headers.user;
595-
let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
596-
Some(emer) => emer,
597-
None => err!("Emergency access not valid."),
598-
};
605+
let emergency_access =
606+
match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await {
607+
Some(emer) => emer,
608+
None => err!("Emergency access not valid."),
609+
};
599610

600611
if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) {
601612
err!("Emergency access not valid.")
@@ -639,10 +650,11 @@ async fn password_emergency_access(
639650
//let key = &data.Key;
640651

641652
let requesting_user = headers.user;
642-
let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
643-
Some(emer) => emer,
644-
None => err!("Emergency access not valid."),
645-
};
653+
let emergency_access =
654+
match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await {
655+
Some(emer) => emer,
656+
None => err!("Emergency access not valid."),
657+
};
646658

647659
if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) {
648660
err!("Emergency access not valid.")
@@ -674,10 +686,11 @@ async fn password_emergency_access(
674686
#[get("/emergency-access/<emer_id>/policies")]
675687
async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn) -> JsonResult {
676688
let requesting_user = headers.user;
677-
let emergency_access = match EmergencyAccess::find_by_uuid(emer_id, &mut conn).await {
678-
Some(emer) => emer,
679-
None => err!("Emergency access not valid."),
680-
};
689+
let emergency_access =
690+
match EmergencyAccess::find_by_uuid_and_grantee_uuid(emer_id, &requesting_user.uuid, &mut conn).await {
691+
Some(emer) => emer,
692+
None => err!("Emergency access not valid."),
693+
};
681694

682695
if !is_valid_request(&emergency_access, &requesting_user.uuid, EmergencyAccessType::Takeover) {
683696
err!("Emergency access not valid.")

src/db/models/emergency_access.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,6 @@ impl EmergencyAccess {
238238
}}
239239
}
240240

241-
pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
242-
db_run! { conn: {
243-
emergency_access::table
244-
.filter(emergency_access::uuid.eq(uuid))
245-
.first::<EmergencyAccessDb>(conn)
246-
.ok().from_db()
247-
}}
248-
}
249-
250241
pub async fn find_by_grantor_uuid_and_grantee_uuid_or_email(
251242
grantor_uuid: &str,
252243
grantee_uuid: &str,
@@ -281,6 +272,26 @@ impl EmergencyAccess {
281272
}}
282273
}
283274

275+
pub async fn find_by_uuid_and_grantee_uuid(uuid: &str, grantee_uuid: &str, conn: &mut DbConn) -> Option<Self> {
276+
db_run! { conn: {
277+
emergency_access::table
278+
.filter(emergency_access::uuid.eq(uuid))
279+
.filter(emergency_access::grantee_uuid.eq(grantee_uuid))
280+
.first::<EmergencyAccessDb>(conn)
281+
.ok().from_db()
282+
}}
283+
}
284+
285+
pub async fn find_by_uuid_and_grantee_email(uuid: &str, grantee_email: &str, conn: &mut DbConn) -> Option<Self> {
286+
db_run! { conn: {
287+
emergency_access::table
288+
.filter(emergency_access::uuid.eq(uuid))
289+
.filter(emergency_access::email.eq(grantee_email))
290+
.first::<EmergencyAccessDb>(conn)
291+
.ok().from_db()
292+
}}
293+
}
294+
284295
pub async fn find_all_by_grantee_uuid(grantee_uuid: &str, conn: &mut DbConn) -> Vec<Self> {
285296
db_run! { conn: {
286297
emergency_access::table

0 commit comments

Comments
 (0)