@ -1,11 +1,11 @@
use chrono ::{ Duration , Utc } ;
use chrono ::{ TimeDelta , Utc } ;
use rocket ::{ serde ::json ::Json , Route } ;
use serde_json ::Value ;
use crate ::{
api ::{
core ::{ CipherSyncData , CipherSyncType } ,
EmptyResult , JsonResult , JsonUpcase ,
EmptyResult , JsonResult ,
} ,
auth ::{ decode_emergency_access_invite , Headers } ,
db ::{ models ::* , DbConn , DbPool } ,
@ -43,31 +43,33 @@ pub fn routes() -> Vec<Route> {
async fn get_contacts ( headers : Headers , mut conn : DbConn ) -> Json < Value > {
if ! CONFIG . emergency_access_allowed ( ) {
return Json ( json ! ( {
"D ata" : [ {
"I d" : "" ,
"S tatus" : 2 ,
"T ype" : 0 ,
"W aitTimeDays" : 0 ,
"G ranteeId" : "" ,
"E mail" : "" ,
"N ame" : "NOTE: Emergency Access is disabled!" ,
"O bject" : "emergencyAccessGranteeDetails" ,
"d ata" : [ {
"i d" : "" ,
"s tatus" : 2 ,
"t ype" : 0 ,
"w aitTimeDays" : 0 ,
"g ranteeId" : "" ,
"e mail" : "" ,
"n ame" : "NOTE: Emergency Access is disabled!" ,
"o bject" : "emergencyAccessGranteeDetails" ,
} ] ,
"O bject" : "list" ,
"C ontinuationToken" : null
"o bject" : "list" ,
"c ontinuationToken" : null
} ) ) ;
}
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 ) ;
if let Some ( grantee ) = ea . to_json_grantee_details ( & mut conn ) . await {
emergency_access_list_json . push ( grantee )
}
}
Json ( json ! ( {
"D ata" : emergency_access_list_json ,
"O bject" : "list" ,
"C ontinuationToken" : null
"d ata" : emergency_access_list_json ,
"o bject" : "list" ,
"c ontinuationToken" : null
} ) )
}
@ -84,18 +86,20 @@ async fn get_grantees(headers: Headers, mut conn: DbConn) -> Json<Value> {
}
Json ( json ! ( {
"D ata" : emergency_access_list_json ,
"O bject" : "list" ,
"C ontinuationToken" : null
"d ata" : emergency_access_list_json ,
"o bject" : "list" ,
"c ontinuationToken" : null
} ) )
}
#[ get( " /emergency-access/<emer_id> " ) ]
async fn get_emergency_access ( emer_id : & str , mut conn : DbConn ) -> JsonResult {
async fn get_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
Some ( emergency_access ) = > Ok ( Json ( emergency_access . to_json_grantee_details ( & mut conn ) . await ) ) ,
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emergency_access ) = > Ok ( Json (
emergency_access . to_json_grantee_details ( & mut conn ) . await . expect ( "Grantee user should exist but does not!" ) ,
) ) ,
None = > err ! ( "Emergency access not valid." ) ,
}
}
@ -105,42 +109,49 @@ async fn get_emergency_access(emer_id: &str, mut conn: DbConn) -> JsonResult {
// region put/post
#[ derive(Deserialize) ]
#[ allow(non_snake_case )]
#[ serde(rename_all = " camelCase " ) ]
struct EmergencyAccessUpdateData {
T ype : NumberOrString ,
WaitTimeD ays : i32 ,
KeyE ncrypted : Option < String > ,
r#t ype : NumberOrString ,
wait_time_d ays : i32 ,
key_e ncrypted : Option < String > ,
}
#[ put( " /emergency-access/<emer_id> " , data = " <data> " ) ]
async fn put_emergency_access ( emer_id : & str , data : JsonUpcase < EmergencyAccessUpdateData > , conn : DbConn ) -> JsonResult {
post_emergency_access ( emer_id , data , conn ) . await
async fn put_emergency_access (
emer_id : & str ,
data : Json < EmergencyAccessUpdateData > ,
headers : Headers ,
conn : DbConn ,
) -> JsonResult {
post_emergency_access ( emer_id , data , headers , conn ) . await
}
#[ post( " /emergency-access/<emer_id> " , data = " <data> " ) ]
async fn post_emergency_access (
emer_id : & str ,
data : JsonUpcase < EmergencyAccessUpdateData > ,
data : Json < EmergencyAccessUpdateData > ,
headers : Headers ,
mut conn : DbConn ,
) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
let data : EmergencyAccessUpdateData = data . into_inner ( ) . data ;
let data : EmergencyAccessUpdateData = data . into_inner ( ) ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emergency_access ) = > emergency_access ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
let new_type = match EmergencyAccessType ::from_str ( & data . T ype. into_string ( ) ) {
let new_type = match EmergencyAccessType ::from_str ( & data . r#t ype. into_string ( ) ) {
Some ( new_type ) = > new_type as i32 ,
None = > err ! ( "Invalid emergency access type." ) ,
} ;
emergency_access . atype = new_type ;
emergency_access . wait_time_days = data . WaitTimeD ays;
if data . KeyE ncrypted. is_some ( ) {
emergency_access . key_encrypted = data . KeyE ncrypted;
emergency_access . wait_time_days = data . wait_time_d ays;
if data . key_e ncrypted. is_some ( ) {
emergency_access . key_encrypted = data . key_e ncrypted;
}
emergency_access . save ( & mut conn ) . await ? ;
@ -155,17 +166,21 @@ async fn post_emergency_access(
async fn delete_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> EmptyResult {
check_emergency_access_enabled ( ) ? ;
let grantor_user = headers . user ;
let emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
Some ( emer ) = > {
if emer . grantor_uuid ! = grantor_user . uuid & & emer . grantee_uuid ! = Some ( grantor_user . uuid ) {
err ! ( "Emergency access not valid." )
let emergency_access = match (
EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await ,
EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await ,
) {
( Some ( grantor_emer ) , None ) = > {
info ! ( "Grantor deleted emergency access {emer_id}" ) ;
grantor_emer
}
emer
( None , Some ( grantee_emer ) ) = > {
info ! ( "Grantee deleted emergency access {emer_id}" ) ;
grantee_emer
}
None = > err ! ( "Emergency access not valid." ) ,
_ = > err ! ( "Emergency access not valid." ) ,
} ;
emergency_access . delete ( & mut conn ) . await ? ;
Ok ( ( ) )
}
@ -180,24 +195,24 @@ async fn post_delete_emergency_access(emer_id: &str, headers: Headers, conn: DbC
// region invite
#[ derive(Deserialize) ]
#[ allow(non_snake_case )]
#[ serde(rename_all = " camelCase " ) ]
struct EmergencyAccessInviteData {
E mail : String ,
T ype : NumberOrString ,
WaitTimeD ays : i32 ,
e mail : String ,
r#t ype : NumberOrString ,
wait_time_d ays : i32 ,
}
#[ post( " /emergency-access/invite " , data = " <data> " ) ]
async fn send_invite ( data : JsonUpcase < EmergencyAccessInviteData > , headers : Headers , mut conn : DbConn ) -> EmptyResult {
async fn send_invite ( data : Json < EmergencyAccessInviteData > , headers : Headers , mut conn : DbConn ) -> EmptyResult {
check_emergency_access_enabled ( ) ? ;
let data : EmergencyAccessInviteData = data . into_inner ( ) . data ;
let email = data . E mail. to_lowercase ( ) ;
let wait_time_days = data . WaitTimeD ays;
let data : EmergencyAccessInviteData = data . into_inner ( ) ;
let email = data . e mail. to_lowercase ( ) ;
let wait_time_days = data . wait_time_d ays;
let emergency_access_status = EmergencyAccessStatus ::Invited as i32 ;
let new_type = match EmergencyAccessType ::from_str ( & data . T ype. into_string ( ) ) {
let new_type = match EmergencyAccessType ::from_str ( & data . r#t ype. into_string ( ) ) {
Some ( new_type ) = > new_type as i32 ,
None = > err ! ( "Invalid emergency access type." ) ,
} ;
@ -209,7 +224,7 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
err ! ( "You can not set yourself as an emergency contact." )
}
let grantee_user = match User ::find_by_mail ( & email , & mut conn ) . await {
let ( grantee_user , new_user ) = match User ::find_by_mail ( & email , & mut conn ) . await {
None = > {
if ! CONFIG . invitations_allowed ( ) {
err ! ( format ! ( "Grantee user does not exist: {}" , & email ) )
@ -226,9 +241,10 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
let mut user = User ::new ( email . clone ( ) ) ;
user . save ( & mut conn ) . await ? ;
user
( user , true )
}
Some ( user ) = > user ,
Some ( user ) if user . password_hash . is_empty ( ) = > ( user , true ) ,
Some ( user ) = > ( user , false ) ,
} ;
if EmergencyAccess ::find_by_grantor_uuid_and_grantee_uuid_or_email (
@ -256,15 +272,9 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
& grantor_user . email ,
)
. await ? ;
} else {
// Automatically mark user as accepted if no email invites
match User ::find_by_mail ( & email , & mut conn ) . await {
Some ( user ) = > match accept_invite_process ( & user . uuid , & mut new_emergency_access , & email , & mut conn ) . await {
Ok ( v ) = > v ,
Err ( e ) = > err ! ( e . to_string ( ) ) ,
} ,
None = > err ! ( "Grantee user not found." ) ,
}
} else if ! new_user {
// if mail is not enabled immediately accept the invitation for existing users
new_emergency_access . accept_invite ( & grantee_user . uuid , & email , & mut conn ) . await ? ;
}
Ok ( ( ) )
@ -274,15 +284,12 @@ async fn send_invite(data: JsonUpcase<EmergencyAccessInviteData>, headers: Heade
async fn resend_invite ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> EmptyResult {
check_emergency_access_enabled ( ) ? ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
if emergency_access . grantor_uuid ! = headers . user . uuid {
err ! ( "Emergency access not valid." ) ;
}
if emergency_access . status ! = EmergencyAccessStatus ::Invited as i32 {
err ! ( "The grantee user is already accepted or confirmed to the organization" ) ;
}
@ -308,34 +315,29 @@ async fn resend_invite(emer_id: &str, headers: Headers, mut conn: DbConn) -> Emp
& grantor_user . email ,
)
. await ? ;
} else {
if Invitation ::find_by_mail ( & email , & mut conn ) . await . is_none ( ) {
} else if ! grantee_user . password_hash . is_empty ( ) {
// accept the invitation for existing user
emergency_access . accept_invite ( & grantee_user . uuid , & email , & mut conn ) . await ? ;
} else if CONFIG . invitations_allowed ( ) & & Invitation ::find_by_mail ( & email , & mut conn ) . await . is_none ( ) {
let invitation = Invitation ::new ( & email ) ;
invitation . save ( & mut conn ) . await ? ;
}
// Automatically mark user as accepted if no email invites
match accept_invite_process ( & grantee_user . uuid , & mut emergency_access , & email , & mut conn ) . await {
Ok ( v ) = > v ,
Err ( e ) = > err ! ( e . to_string ( ) ) ,
}
}
Ok ( ( ) )
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case )]
#[ serde(rename_all = " camelCase " ) ]
struct AcceptData {
T oken : String ,
token : String ,
}
#[ post( " /emergency-access/<emer_id>/accept " , data = " <data> " ) ]
async fn accept_invite ( emer_id : & str , data : JsonUpcase < AcceptData > , headers : Headers , mut conn : DbConn ) -> EmptyResult {
async fn accept_invite ( emer_id : & str , data : Json < AcceptData > , headers : Headers , mut conn : DbConn ) -> EmptyResult {
check_emergency_access_enabled ( ) ? ;
let data : AcceptData = data . into_inner ( ) . data ;
let token = & data . T oken;
let data : AcceptData = data . into_inner ( ) ;
let token = & data . t oken;
let claims = decode_emergency_access_invite ( token ) ? ;
// This can happen if the user who received the invite used a different email to signup.
@ -352,7 +354,10 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase<AcceptData>, headers: Hea
None = > err ! ( "Invited user not found" ) ,
} ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
// 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.
// The uuid of the grantee gets stored once accepted.
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_email ( emer_id , & headers . user . email , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -367,10 +372,7 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase<AcceptData>, headers: Hea
& & grantor_user . name = = claims . grantor_name
& & grantor_user . email = = claims . grantor_email
{
match accept_invite_process ( & grantee_user . uuid , & mut emergency_access , & grantee_user . email , & mut conn ) . await {
Ok ( v ) = > v ,
Err ( e ) = > err ! ( e . to_string ( ) ) ,
}
emergency_access . accept_invite ( & grantee_user . uuid , & grantee_user . email , & mut conn ) . await ? ;
if CONFIG . mail_enabled ( ) {
mail ::send_emergency_access_invite_accepted ( & grantor_user . email , & grantee_user . email ) . await ? ;
@ -382,46 +384,27 @@ async fn accept_invite(emer_id: &str, data: JsonUpcase<AcceptData>, headers: Hea
}
}
async fn accept_invite_process (
grantee_uuid : & str ,
emergency_access : & mut EmergencyAccess ,
grantee_email : & str ,
conn : & mut DbConn ,
) -> EmptyResult {
if emergency_access . email . is_none ( ) | | emergency_access . email . as_ref ( ) . unwrap ( ) ! = grantee_email {
err ! ( "User email does not match invite." ) ;
}
if emergency_access . status = = EmergencyAccessStatus ::Accepted as i32 {
err ! ( "Emergency contact already accepted." ) ;
}
emergency_access . status = EmergencyAccessStatus ::Accepted as i32 ;
emergency_access . grantee_uuid = Some ( String ::from ( grantee_uuid ) ) ;
emergency_access . email = None ;
emergency_access . save ( conn ) . await
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case )]
#[ serde(rename_all = " camelCase " ) ]
struct ConfirmData {
K ey : String ,
key : String ,
}
#[ post( " /emergency-access/<emer_id>/confirm " , data = " <data> " ) ]
async fn confirm_emergency_access (
emer_id : & str ,
data : JsonUpcase < ConfirmData > ,
data : Json < ConfirmData > ,
headers : Headers ,
mut conn : DbConn ,
) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
let confirming_user = headers . user ;
let data : ConfirmData = data . into_inner ( ) . data ;
let key = data . K ey;
let data : ConfirmData = data . into_inner ( ) ;
let key = data . key ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & confirming_user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -467,14 +450,13 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
check_emergency_access_enabled ( ) ? ;
let initiating_user = headers . user ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & initiating_user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
if emergency_access . status ! = EmergencyAccessStatus ::Confirmed as i32
| | emergency_access . grantee_uuid ! = Some ( initiating_user . uuid )
{
if emergency_access . status ! = EmergencyAccessStatus ::Confirmed as i32 {
err ! ( "Emergency access not valid." )
}
@ -506,14 +488,13 @@ async fn initiate_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
async fn approve_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
if emergency_access . status ! = EmergencyAccessStatus ::RecoveryInitiated as i32
| | emergency_access . grantor_uuid ! = headers . user . uuid
{
if emergency_access . status ! = EmergencyAccessStatus ::RecoveryInitiated as i32 {
err ! ( "Emergency access not valid." )
}
@ -544,23 +525,18 @@ async fn approve_emergency_access(emer_id: &str, headers: Headers, mut conn: DbC
async fn reject_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
let mut emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let mut emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantor_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
if ( emergency_access . status ! = EmergencyAccessStatus ::RecoveryInitiated as i32
& & emergency_access . status ! = EmergencyAccessStatus ::RecoveryApproved as i32 )
| | emergency_access . grantor_uuid ! = headers . user . uuid
if emergency_access . status ! = EmergencyAccessStatus ::RecoveryInitiated as i32
& & emergency_access . status ! = EmergencyAccessStatus ::RecoveryApproved as i32
{
err ! ( "Emergency access not valid." )
}
let grantor_user = match User ::find_by_uuid ( & headers . user . uuid , & mut conn ) . await {
Some ( user ) = > user ,
None = > err ! ( "Grantor user not found." ) ,
} ;
if let Some ( grantee_uuid ) = emergency_access . grantee_uuid . as_ref ( ) {
let grantee_user = match User ::find_by_uuid ( grantee_uuid , & mut conn ) . await {
Some ( user ) = > user ,
@ -571,7 +547,7 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo
emergency_access . save ( & mut conn ) . await ? ;
if CONFIG . mail_enabled ( ) {
mail ::send_emergency_access_recovery_rejected ( & grantee_user . email , & grantor_ user. name ) . await ? ;
mail ::send_emergency_access_recovery_rejected ( & grantee_user . email , & headers . user . name ) . await ? ;
}
Ok ( Json ( emergency_access . to_json ( ) ) )
} else {
@ -587,7 +563,8 @@ async fn reject_emergency_access(emer_id: &str, headers: Headers, mut conn: DbCo
async fn view_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> JsonResult {
check_emergency_access_enabled ( ) ? ;
let emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & headers . user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -614,9 +591,9 @@ async fn view_emergency_access(emer_id: &str, headers: Headers, mut conn: DbConn
}
Ok ( Json ( json ! ( {
"C iphers" : ciphers_json ,
"K eyEncrypted" : & emergency_access . key_encrypted ,
"O bject" : "emergencyAccessView" ,
"c iphers" : ciphers_json ,
"k eyEncrypted" : & emergency_access . key_encrypted ,
"o bject" : "emergencyAccessView" ,
} ) ) )
}
@ -625,7 +602,8 @@ async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
check_emergency_access_enabled ( ) ? ;
let requesting_user = headers . user ;
let emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & requesting_user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -640,39 +618,40 @@ async fn takeover_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
} ;
let result = json ! ( {
"K df" : grantor_user . client_kdf_type ,
"K dfIterations" : grantor_user . client_kdf_iter ,
"K dfMemory" : grantor_user . client_kdf_memory ,
"K dfParallelism" : grantor_user . client_kdf_parallelism ,
"K eyEncrypted" : & emergency_access . key_encrypted ,
"O bject" : "emergencyAccessTakeover" ,
"k df" : grantor_user . client_kdf_type ,
"k dfIterations" : grantor_user . client_kdf_iter ,
"k dfMemory" : grantor_user . client_kdf_memory ,
"k dfParallelism" : grantor_user . client_kdf_parallelism ,
"k eyEncrypted" : & emergency_access . key_encrypted ,
"o bject" : "emergencyAccessTakeover" ,
} ) ;
Ok ( Json ( result ) )
}
#[ derive(Deserialize) ]
#[ allow(non_snake_case )]
#[ serde(rename_all = " camelCase " ) ]
struct EmergencyAccessPasswordData {
NewMasterPasswordH ash : String ,
K ey : String ,
new_master_password_h ash : String ,
k ey : String ,
}
#[ post( " /emergency-access/<emer_id>/password " , data = " <data> " ) ]
async fn password_emergency_access (
emer_id : & str ,
data : JsonUpcase < EmergencyAccessPasswordData > ,
data : Json < EmergencyAccessPasswordData > ,
headers : Headers ,
mut conn : DbConn ,
) -> EmptyResult {
check_emergency_access_enabled ( ) ? ;
let data : EmergencyAccessPasswordData = data . into_inner ( ) . data ;
let new_master_password_hash = & data . NewMasterPasswordH ash;
let data : EmergencyAccessPasswordData = data . into_inner ( ) ;
let new_master_password_hash = & data . new_master_password_h ash;
//let key = &data.Key;
let requesting_user = headers . user ;
let emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & requesting_user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -687,7 +666,7 @@ async fn password_emergency_access(
} ;
// change grantor_user password
grantor_user . set_password ( new_master_password_hash , Some ( data . K ey) , true , None ) ;
grantor_user . set_password ( new_master_password_hash , Some ( data . k ey) , true , None ) ;
grantor_user . save ( & mut conn ) . await ? ;
// Disable TwoFactor providers since they will otherwise block logins
@ -707,7 +686,8 @@ async fn password_emergency_access(
#[ get( " /emergency-access/<emer_id>/policies " ) ]
async fn policies_emergency_access ( emer_id : & str , headers : Headers , mut conn : DbConn ) -> JsonResult {
let requesting_user = headers . user ;
let emergency_access = match EmergencyAccess ::find_by_uuid ( emer_id , & mut conn ) . await {
let emergency_access =
match EmergencyAccess ::find_by_uuid_and_grantee_uuid ( emer_id , & requesting_user . uuid , & mut conn ) . await {
Some ( emer ) = > emer ,
None = > err ! ( "Emergency access not valid." ) ,
} ;
@ -725,9 +705,9 @@ async fn policies_emergency_access(emer_id: &str, headers: Headers, mut conn: Db
let policies_json : Vec < Value > = policies . await . iter ( ) . map ( OrgPolicy ::to_json ) . collect ( ) ;
Ok ( Json ( json ! ( {
"D ata" : policies_json ,
"O bject" : "list" ,
"C ontinuationToken" : null
"d ata" : policies_json ,
"o bject" : "list" ,
"c ontinuationToken" : null
} ) ) )
}
@ -766,7 +746,7 @@ pub async fn emergency_request_timeout_job(pool: DbPool) {
for mut emer in emergency_access_list {
// 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 ) ) ;
emer . recovery_initiated_at . unwrap ( ) + TimeDelta ::try_ days( i64 ::from ( emer . wait_time_days ) ) . unwrap ( ) ;
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
@ -822,10 +802,10 @@ pub async fn emergency_notification_reminder_job(pool: DbPool) {
// 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 ) ) ;
emer . recovery_initiated_at . unwrap ( ) + TimeDelta ::try_ days( i64 ::from ( emer . wait_time_days - 1 ) ) . unwrap ( ) ;
// 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 )
last_notification_at + TimeDelta ::try_ days( 1 ) . unwrap ( )
} else {
now
} ;