diff --git a/.env.template b/.env.template index 705c533c1b..4dd8c585e1 100644 --- a/.env.template +++ b/.env.template @@ -61,6 +61,10 @@ ## To control this on a per-org basis instead, use the "Disable Send" org policy. # SENDS_ALLOWED=true +## Controls whether users can enable emergency access to their accounts. +## This setting applies globally to all users. +# EMERGENCY_ACCESS_ALLOWED=true + ## Job scheduler settings ## ## Job schedules use a cron-like syntax (as parsed by https://crates.io/crates/cron), @@ -78,13 +82,13 @@ ## Defaults to daily (5 minutes after midnight). Set blank to disable this job. # TRASH_PURGE_SCHEDULE="0 5 0 * * *" ## -## Cron schedule of the job that sends expiration reminders to emergency request grantors. -## Defaults to hourly (10 minutes after the hour). Set blank to disable this job. -# EMERGENCY_NOTIFICATION_REMINDER_SCHEDULE="0 10 * * * *" +## Cron schedule of the job that sends expiration reminders to emergency access grantors. +## Defaults to hourly (5 minutes after the hour). Set blank to disable this job. +# EMERGENCY_NOTIFICATION_REMINDER_SCHEDULE="0 5 * * * *" ## -## Cron schedule of the job that checks for expired (i.e granted by timeout) emergency requests. -## Defaults to hourly (15 minutes after the hour). Set blank to disable this job. -# EMERGENCY_REQUEST_TIMEOUT_SCHEDULE="0 15 * * * *" +## Cron schedule of the job that grants emergency access requests that have met the required wait time. +## Defaults to hourly (5 minutes after the hour). Set blank to disable this job. +# EMERGENCY_REQUEST_TIMEOUT_SCHEDULE="0 5 * * * *" ## Enable extended logging, which shows timestamps and targets in the logs # EXTENDED_LOGGING=true @@ -320,9 +324,6 @@ ## If sending the email fails the login attempt will fail!! # REQUIRE_DEVICE_EMAIL=false -## Emergency access enable. Enable or disable the emergency access feature for all users -# EMERGENCY_ACCESS_ALLOWED=false - ## HIBP Api Key ## HaveIBeenPwned API Key, request it here: https://haveibeenpwned.com/API/Key # HIBP_API_KEY= diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 4e613a373d..936368894a 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -91,10 +91,9 @@ fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { user } else if CONFIG.is_signup_allowed(&email) { // check if it's invited by emergency contact - if EmergencyAccess::find_invited_by_grantee_email(&data.Email, &conn).is_some() { - user - } else { - err!("Account with this email already exists") + match EmergencyAccess::find_invited_by_grantee_email(&data.Email, &conn) { + Some(_) => user, + _ => err!("Account with this email already exists"), } } else { err!("Registration not allowed or user already exists") diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 50c885e451..439c9ba465 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -464,7 +464,7 @@ fn initiate_emergency_access(emer_id: String, headers: Headers, conn: DbConn) -> mail::send_emergency_access_recovery_initiated( &grantor_user.email, &initiating_user.name, - emergency_access.get_atype_as_str(), + emergency_access.get_type_as_str(), &emergency_access.wait_time_days.clone().to_string(), )?; } @@ -743,7 +743,7 @@ pub fn emergency_request_timeout_job(pool: DbPool) { mail::send_emergency_access_recovery_timed_out( &grantor_user.email, &grantee_user.name.clone(), - emer.get_atype_as_str(), + emer.get_type_as_str(), ) .expect("Error on sending email"); @@ -792,8 +792,8 @@ pub fn emergency_notification_reminder_job(pool: DbPool) { mail::send_emergency_access_recovery_reminder( &grantor_user.email, &grantee_user.name.clone(), - emer.get_atype_as_str(), - &emer.wait_time_days.to_string(), + emer.get_type_as_str(), + &emer.wait_time_days.to_string(), // TODO(jjlin): This should be the number of days left. ) .expect("Error on sending email"); } diff --git a/src/config.rs b/src/config.rs index d4b418bc04..1315d45771 100644 --- a/src/config.rs +++ b/src/config.rs @@ -333,12 +333,12 @@ make_config! { /// Trash purge schedule |> Cron schedule of the job that checks for trashed items to delete permanently. /// Defaults to daily. Set blank to disable this job. trash_purge_schedule: String, false, def, "0 5 0 * * *".to_string(); - /// Emergency notification reminder schedule |> Cron schedule of the job that sends expiration reminders to emergency request grantors. + /// Emergency notification reminder schedule |> Cron schedule of the job that sends expiration reminders to emergency access grantors. /// Defaults to hourly. Set blank to disable this job. - emergency_notification_reminder_schedule: String, false, def, "0 10 * * * *".to_string(); - /// Emergency request timeout schedule |> Cron schedule of the job that checks for expired (i.e granted by timeout) emergency requests. + emergency_notification_reminder_schedule: String, false, def, "0 5 * * * *".to_string(); + /// Emergency request timeout schedule |> Cron schedule of the job that grants emergency access requests that have met the required wait time. /// Defaults to hourly. Set blank to disable this job. - emergency_request_timeout_schedule: String, false, def, "0 15 * * * *".to_string(); + emergency_request_timeout_schedule: String, false, def, "0 5 * * * *".to_string(); }, /// General settings @@ -391,7 +391,7 @@ make_config! { org_creation_users: String, true, def, "".to_string(); /// Allow invitations |> Controls whether users can be invited by organization admins, even when signups are otherwise disabled invitations_allowed: bool, true, def, true; - /// Allow emergency access |> Controls whether users can enable emergency access to their accounts + /// Allow emergency access |> Controls whether users can enable emergency access to their accounts. This setting applies globally to all users. emergency_access_allowed: bool, true, def, true; /// Password iterations |> Number of server-side passwords hashing iterations. /// The changes only apply when a user changes their password. Not recommended to lower the value diff --git a/src/db/models/emergency_access.rs b/src/db/models/emergency_access.rs index 94822108ee..7327eb34b2 100644 --- a/src/db/models/emergency_access.rs +++ b/src/db/models/emergency_access.rs @@ -47,7 +47,7 @@ impl EmergencyAccess { } } - pub fn get_atype_as_str(&self) -> &'static str { + pub fn get_type_as_str(&self) -> &'static str { if self.atype == EmergencyAccessType::View as i32 { "View" } else { @@ -55,6 +55,14 @@ impl EmergencyAccess { } } + pub fn has_type(&self, access_type: EmergencyAccessType) -> bool { + self.atype == access_type as i32 + } + + pub fn has_status(&self, status: EmergencyAccessStatus) -> bool { + self.status == status as i32 + } + pub fn to_json(&self) -> Value { json!({ "Id": self.uuid, @@ -66,55 +74,40 @@ impl EmergencyAccess { } pub fn to_json_grantor_details(&self, conn: &DbConn) -> Value { - // find grantor - let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).unwrap(); + let grantor_user = User::find_by_uuid(&self.grantor_uuid, conn).expect("Grantor user not found."); + json!({ - "Id": self.uuid, + "Id": self.uuid, "Status": self.status, "Type": self.atype, "WaitTimeDays": self.wait_time_days, "GrantorId": grantor_user.uuid, "Email": grantor_user.email, "Name": grantor_user.name, - "Object": "emergencyAccessGrantorDetails",}) + "Object": "emergencyAccessGrantorDetails", + }) } + #[allow(clippy::manual_map)] pub fn to_json_grantee_details(&self, conn: &DbConn) -> Value { - if self.grantee_uuid.is_some() { - let grantee_user = - User::find_by_uuid(&self.grantee_uuid.clone().unwrap(), conn).expect("Grantee user not found."); - - json!({ - "Id": self.uuid, - "Status": self.status, - "Type": self.atype, - "WaitTimeDays": self.wait_time_days, - "GranteeId": grantee_user.uuid, - "Email": grantee_user.email, - "Name": grantee_user.name, - "Object": "emergencyAccessGranteeDetails",}) - } else if self.email.is_some() { - let grantee_user = User::find_by_mail(&self.email.clone().unwrap(), conn).expect("Grantee user not found."); - json!({ - "Id": self.uuid, - "Status": self.status, - "Type": self.atype, - "WaitTimeDays": self.wait_time_days, - "GranteeId": grantee_user.uuid, - "Email": grantee_user.email, - "Name": grantee_user.name, - "Object": "emergencyAccessGranteeDetails",}) + let grantee_user = if let Some(grantee_uuid) = self.grantee_uuid.as_deref() { + Some(User::find_by_uuid(grantee_uuid, conn).expect("Grantee user not found.")) + } else if let Some(email) = self.email.as_deref() { + Some(User::find_by_mail(email, conn).expect("Grantee user not found.")) } else { - json!({ - "Id": self.uuid, - "Status": self.status, - "Type": self.atype, - "WaitTimeDays": self.wait_time_days, - "GranteeId": "", - "Email": "", - "Name": "", - "Object": "emergencyAccessGranteeDetails",}) - } + None + }; + + json!({ + "Id": self.uuid, + "Status": self.status, + "Type": self.atype, + "WaitTimeDays": self.wait_time_days, + "GranteeId": grantee_user.as_ref().map_or("", |u| &u.uuid), + "Email": grantee_user.as_ref().map_or("", |u| &u.email), + "Name": grantee_user.as_ref().map_or("", |u| &u.name), + "Object": "emergencyAccessGranteeDetails", + }) } } @@ -198,11 +191,11 @@ impl EmergencyAccess { } pub fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { - for user_org in Self::find_all_by_grantor_uuid(user_uuid, conn) { - user_org.delete(conn)?; + for ea in Self::find_all_by_grantor_uuid(user_uuid, conn) { + ea.delete(conn)?; } - for user_org in Self::find_all_by_grantee_uuid(user_uuid, conn) { - user_org.delete(conn)?; + for ea in Self::find_all_by_grantee_uuid(user_uuid, conn) { + ea.delete(conn)?; } Ok(()) } @@ -213,7 +206,7 @@ impl EmergencyAccess { db_run! { conn: { diesel::delete(emergency_access::table.filter(emergency_access::uuid.eq(self.uuid))) .execute(conn) - .map_res("Error removing user from organization") + .map_res("Error removing user from emergency access") }} } @@ -246,7 +239,6 @@ impl EmergencyAccess { emergency_access::table .filter(emergency_access::status.eq(EmergencyAccessStatus::RecoveryInitiated as i32)) .load::(conn).expect("Error loading emergency_access").from_db() - }} } diff --git a/src/mail.rs b/src/mail.rs index 4c163e38a6..f81f3cb2d3 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -329,7 +329,7 @@ pub fn send_emergency_access_recovery_reminder( address: &str, grantee_name: &str, atype: &str, - wait_time_days: &str, + days_left: &str, ) -> EmptyResult { let (subject, body_html, body_text) = get_text( "email/emergency_access_recovery_reminder", @@ -337,7 +337,7 @@ pub fn send_emergency_access_recovery_reminder( "url": CONFIG.domain(), "grantee_name": grantee_name, "atype": atype, - "wait_time_days": wait_time_days, + "days_left": days_left, }), )?; diff --git a/src/main.rs b/src/main.rs index eeabdf28ea..11a67174e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -345,12 +345,17 @@ fn schedule_jobs(pool: db::DbPool) { })); } + // Grant emergency access requests that have met the required wait time. + // This job should run before the emergency access reminders job to avoid + // sending reminders for requests that are about to be granted anyway. if !CONFIG.emergency_request_timeout_schedule().is_empty() { sched.add(Job::new(CONFIG.emergency_request_timeout_schedule().parse().unwrap(), || { api::emergency_request_timeout_job(pool.clone()); })); } + // Send reminders to emergency access grantors that there are pending + // emergency access requests. if !CONFIG.emergency_notification_reminder_schedule().is_empty() { sched.add(Job::new(CONFIG.emergency_notification_reminder_schedule().parse().unwrap(), || { api::emergency_notification_reminder_job(pool.clone()); @@ -362,6 +367,10 @@ fn schedule_jobs(pool: db::DbPool) { // interval of 30 seconds should be sufficient. Users who want to // schedule jobs to run more frequently for some reason can reduce // the poll interval accordingly. + // + // Note that the scheduler checks jobs in the order in which they + // were added, so if two jobs are both eligible to run at a given + // tick, the one that was added earlier will run first. loop { sched.tick(); thread::sleep(Duration::from_millis(CONFIG.job_poll_interval_ms())); diff --git a/src/static/templates/email/emergency_access_invite_accepted.hbs b/src/static/templates/email/emergency_access_invite_accepted.hbs index 9f31668091..88382b1b53 100644 --- a/src/static/templates/email/emergency_access_invite_accepted.hbs +++ b/src/static/templates/email/emergency_access_invite_accepted.hbs @@ -1,8 +1,8 @@ -Emergency contact {{{grantee_email}}} accepted +Emergency access contact {{{grantee_email}}} accepted This email is to notify you that {{grantee_email}} has accepted your invitation to become an emergency access contact. -To confirm this user, Log into {{url}} the Bitwarden web vault, go to settings and confirm the user. +To confirm this user, log into the web vault ({{url}}), go to settings and confirm the user. If you do not wish to confirm this user, you can also remove them on the same page. {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_invite_accepted.html.hbs b/src/static/templates/email/emergency_access_invite_accepted.html.hbs index 37d30b5d4e..003b0c1db6 100644 --- a/src/static/templates/email/emergency_access_invite_accepted.html.hbs +++ b/src/static/templates/email/emergency_access_invite_accepted.html.hbs @@ -1,4 +1,4 @@ -Emergency contact {{{grantee_email}}} accepted +Emergency access contact {{{grantee_email}}} accepted {{> email/email_header }} @@ -9,7 +9,7 @@ Emergency contact {{{grantee_email}}} accepted @@ -18,4 +18,4 @@ Emergency contact {{{grantee_email}}} accepted
- To confirm this user, log into the vaultwarden web vault, go to settings and confirm the user. + To confirm this user, log into the web vault, go to settings and confirm the user.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_invite_confirmed.hbs b/src/static/templates/email/emergency_access_invite_confirmed.hbs index 0510cca283..1417bb6ba3 100644 --- a/src/static/templates/email/emergency_access_invite_confirmed.hbs +++ b/src/static/templates/email/emergency_access_invite_confirmed.hbs @@ -1,6 +1,6 @@ -Emergency contact for {{{grantor_name}}} confirmed +Emergency access contact for {{{grantor_name}}} confirmed -This email is to notify you that you have been confirmed as an emergency access contact for *{{grantor_name}}* was confirmed. +This email is to notify you that you have been confirmed as an emergency access contact for *{{grantor_name}}*. -You can now initiate emergency access requests from the web vault. Log in {{url}}. +You can now initiate emergency access requests from the web vault ({{url}}). {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_invite_confirmed.html.hbs b/src/static/templates/email/emergency_access_invite_confirmed.html.hbs index 52a024ad69..a8dd6f62d7 100644 --- a/src/static/templates/email/emergency_access_invite_confirmed.html.hbs +++ b/src/static/templates/email/emergency_access_invite_confirmed.html.hbs @@ -1,17 +1,16 @@ -Emergency contact for {{{grantor_name}}} confirmed +Emergency access contact for {{{grantor_name}}} confirmed {{> email/email_header }}
- This email is to notify you that you have been confirmed as an emergency access contact for {{grantor_name}} was confirmed. + This email is to notify you that you have been confirmed as an emergency access contact for {{grantor_name}}.
- You can now initiate emergency access requests from the web vault.
- Log in + You can now initiate emergency access requests from the web vault.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_recovery_approved.hbs b/src/static/templates/email/emergency_access_recovery_approved.hbs index 03921e7804..8c2a7cc948 100644 --- a/src/static/templates/email/emergency_access_recovery_approved.hbs +++ b/src/static/templates/email/emergency_access_recovery_approved.hbs @@ -1,4 +1,4 @@ -Emergency contact request for {{{grantor_name}}} approved +Emergency access request for {{{grantor_name}}} approved -{{grantor_name}} has approved your emergency request. You may now login {{url}} on the web vault and access their account. +{{grantor_name}} has approved your emergency access request. You may now login on the web vault ({{url}}) and access their account. {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_recovery_approved.html.hbs b/src/static/templates/email/emergency_access_recovery_approved.html.hbs index e9efdd8840..90875f03ed 100644 --- a/src/static/templates/email/emergency_access_recovery_approved.html.hbs +++ b/src/static/templates/email/emergency_access_recovery_approved.html.hbs @@ -1,11 +1,11 @@ -Emergency contact for {{{grantor_name}}} approved +Emergency access request for {{{grantor_name}}} approved {{> email/email_header }}
- {{grantor_name}} has approved your emergency request. You may now login on the web vault and access their account. + {{grantor_name}} has approved your emergency access request. You may now login on the web vault and access their account.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_recovery_initiated.hbs b/src/static/templates/email/emergency_access_recovery_initiated.hbs index 03b004eb94..072428ac2c 100644 --- a/src/static/templates/email/emergency_access_recovery_initiated.hbs +++ b/src/static/templates/email/emergency_access_recovery_initiated.hbs @@ -1,6 +1,6 @@ Emergency access request by {{{grantee_name}}} initiated -{{grantee_name}} has initiated an emergency request to *{{atype}}* your account. You may login on the web vault and manually approve or reject this request. +{{grantee_name}} has initiated an emergency access request to {{atype}} your account. You may login on the web vault ({{url}}) and manually approve or reject this request. If you do nothing, the request will automatically be approved after {{wait_time_days}} day(s). {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_recovery_initiated.html.hbs b/src/static/templates/email/emergency_access_recovery_initiated.html.hbs index abc9d9c06c..3d847798f6 100644 --- a/src/static/templates/email/emergency_access_recovery_initiated.html.hbs +++ b/src/static/templates/email/emergency_access_recovery_initiated.html.hbs @@ -4,7 +4,7 @@ Emergency access request by {{{grantee_name}}} initiated @@ -13,4 +13,4 @@ Emergency access request by {{{grantee_name}}} initiated
- {{grantee_name}} has initiated an emergency request to {{atype}} your account. You may login on the web vault and manually approve or reject this request. + {{grantee_name}} has initiated an emergency access request to {{atype}} your account. You may login on the web vault and manually approve or reject this request.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_recovery_rejected.hbs b/src/static/templates/email/emergency_access_recovery_rejected.hbs index db886fe2de..674a2e5fc2 100644 --- a/src/static/templates/email/emergency_access_recovery_rejected.hbs +++ b/src/static/templates/email/emergency_access_recovery_rejected.hbs @@ -1,4 +1,4 @@ Emergency access request to {{{grantor_name}}} rejected -{{grantor_name}} has rejected your emergency request. +{{grantor_name}} has rejected your emergency access request. {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_recovery_rejected.html.hbs b/src/static/templates/email/emergency_access_recovery_rejected.html.hbs index 35f71b3c35..92fbd59855 100644 --- a/src/static/templates/email/emergency_access_recovery_rejected.html.hbs +++ b/src/static/templates/email/emergency_access_recovery_rejected.html.hbs @@ -4,8 +4,8 @@ Emergency access request to {{{grantor_name}}} rejected
- {{grantor_name}} has rejected your emergency request. + {{grantor_name}} has rejected your emergency access request.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_recovery_reminder.hbs b/src/static/templates/email/emergency_access_recovery_reminder.hbs index 89b805d343..c2f912f591 100644 --- a/src/static/templates/email/emergency_access_recovery_reminder.hbs +++ b/src/static/templates/email/emergency_access_recovery_reminder.hbs @@ -1,6 +1,6 @@ Emergency access request by {{{grantee_name}}} is pending -{{grantee_name}} has a pending emergency request to *{{atype}}* your account. You may login on the web vault and manually approve or reject this request. +{{grantee_name}} has a pending emergency access request to {{atype}} your account. You may login on the web vault ({{url}}) and manually approve or reject this request. -If you do nothing, the request will automatically be approved after {{wait_time_days}} day(s). +If you do nothing, the request will automatically be approved after {{days_left}} day(s). {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_recovery_reminder.html.hbs b/src/static/templates/email/emergency_access_recovery_reminder.html.hbs index 08cbeb595e..ce2d6b378f 100644 --- a/src/static/templates/email/emergency_access_recovery_reminder.html.hbs +++ b/src/static/templates/email/emergency_access_recovery_reminder.html.hbs @@ -4,13 +4,13 @@ Emergency access request by {{{grantee_name}}} is pending
- {{grantee_name}} has a pending emergency request to {{atype}} your account. You may login on the web vault and manually approve or reject this request. + {{grantee_name}} has a pending emergency access request to {{atype}} your account. You may login on the web vault and manually approve or reject this request.
- If you do nothing, the request will automatically be approved after {{wait_time_days}} day(s). + If you do nothing, the request will automatically be approved after {{days_left}} day(s).
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }} diff --git a/src/static/templates/email/emergency_access_recovery_timed_out.hbs b/src/static/templates/email/emergency_access_recovery_timed_out.hbs index 510ea376f0..a4d564c889 100644 --- a/src/static/templates/email/emergency_access_recovery_timed_out.hbs +++ b/src/static/templates/email/emergency_access_recovery_timed_out.hbs @@ -1,4 +1,4 @@ Emergency access request by {{{grantee_name}}} granted -{{grantee_name}} has been granted emergency request to *{{atype}}* your account. You may login on the web vault and manually revoke this request. +{{grantee_name}} has been granted emergency access to {{atype}} your account. You may login on the web vault ({{url}}) and manually revoke this request. {{> email/email_footer_text }} diff --git a/src/static/templates/email/emergency_access_recovery_timed_out.html.hbs b/src/static/templates/email/emergency_access_recovery_timed_out.html.hbs index 612bbd5a84..74918bbeb7 100644 --- a/src/static/templates/email/emergency_access_recovery_timed_out.html.hbs +++ b/src/static/templates/email/emergency_access_recovery_timed_out.html.hbs @@ -4,8 +4,8 @@ Emergency access request by {{{grantee_name}}} granted
- {{grantee_name}} has been granted emergency request to {{atype}} your account. You may login on the web vault and manually revoke this request. + {{grantee_name}} has been granted emergency access to {{atype}} your account. You may login on the web vault and manually revoke this request.
-{{> email/email_footer }} \ No newline at end of file +{{> email/email_footer }}