diff options
17 files changed, 57 insertions, 21 deletions
diff --git a/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql new file mode 100644 index 00000000..dd0394ee --- /dev/null +++ b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/down.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`; diff --git a/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql new file mode 100644 index 00000000..423e16c1 --- /dev/null +++ b/migrations/mysql/2024-09-04-091351_use_device_type_for_mails/up.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL; diff --git a/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql new file mode 100644 index 00000000..dd0394ee --- /dev/null +++ b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/down.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`; diff --git a/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql new file mode 100644 index 00000000..423e16c1 --- /dev/null +++ b/migrations/postgresql/2024-09-04-091351_use_device_type_for_mails/up.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL; diff --git a/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql new file mode 100644 index 00000000..dd0394ee --- /dev/null +++ b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/down.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` DROP COLUMN `device_type`; diff --git a/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql new file mode 100644 index 00000000..423e16c1 --- /dev/null +++ b/migrations/sqlite/2024-09-04-091351_use_device_type_for_mails/up.sql @@ -0,0 +1 @@ +ALTER TABLE `twofactor_incomplete` ADD COLUMN `device_type` INTEGER NOT NULL; diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs index 86443a5e..e3795eb8 100644 --- a/src/api/core/two_factor/mod.rs +++ b/src/api/core/two_factor/mod.rs @@ -269,8 +269,14 @@ pub async fn send_incomplete_2fa_notifications(pool: DbPool) { "User {} did not complete a 2FA login within the configured time limit. IP: {}", user.email, login.ip_address ); - match mail::send_incomplete_2fa_login(&user.email, &login.ip_address, &login.login_time, &login.device_name) - .await + match mail::send_incomplete_2fa_login( + &user.email, + &login.ip_address, + &login.login_time, + &login.device_name, + &DeviceType::from_i32(login.device_type).to_string(), + ) + .await { Ok(_) => { if let Err(e) = login.delete(&mut conn).await { diff --git a/src/api/identity.rs b/src/api/identity.rs index 27f3eac6..4244d68d 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -265,7 +265,7 @@ async fn _password_login( let twofactor_token = twofactor_auth(&user, &data, &mut device, ip, conn).await?; if CONFIG.mail_enabled() && new_device { - if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await { + if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await { error!("Error sending new device email: {:#?}", e); if CONFIG.require_device_email() { @@ -421,7 +421,7 @@ async fn _user_api_key_login( if CONFIG.mail_enabled() && new_device { let now = Utc::now().naive_utc(); - if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device.name).await { + if let Err(e) = mail::send_new_device_logged_in(&user.email, &ip.ip.to_string(), &now, &device).await { error!("Error sending new device email: {:#?}", e); if CONFIG.require_device_email() { @@ -535,7 +535,7 @@ async fn twofactor_auth( return Ok(None); } - TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, ip, conn).await?; + TwoFactorIncomplete::mark_incomplete(&user.uuid, &device.uuid, &device.name, device.atype, ip, conn).await?; let twofactor_ids: Vec<_> = twofactors.iter().map(|tf| tf.atype).collect(); let selected_id = data.two_factor_provider.unwrap_or(twofactor_ids[0]); // If we aren't given a two factor provider, assume the first one diff --git a/src/db/models/two_factor_incomplete.rs b/src/db/models/two_factor_incomplete.rs index 49f7691f..12813eb5 100644 --- a/src/db/models/two_factor_incomplete.rs +++ b/src/db/models/two_factor_incomplete.rs @@ -13,6 +13,7 @@ db_object! { // must complete 2FA login before being added into the devices table. pub device_uuid: String, pub device_name: String, + pub device_type: i32, pub login_time: NaiveDateTime, pub ip_address: String, } @@ -23,6 +24,7 @@ impl TwoFactorIncomplete { user_uuid: &str, device_uuid: &str, device_name: &str, + device_type: i32, ip: &ClientIp, conn: &mut DbConn, ) -> EmptyResult { @@ -44,6 +46,7 @@ impl TwoFactorIncomplete { twofactor_incomplete::user_uuid.eq(user_uuid), twofactor_incomplete::device_uuid.eq(device_uuid), twofactor_incomplete::device_name.eq(device_name), + twofactor_incomplete::device_type.eq(device_type), twofactor_incomplete::login_time.eq(Utc::now().naive_utc()), twofactor_incomplete::ip_address.eq(ip.ip.to_string()), )) diff --git a/src/db/schemas/mysql/schema.rs b/src/db/schemas/mysql/schema.rs index 58ec55a2..fa84ed05 100644 --- a/src/db/schemas/mysql/schema.rs +++ b/src/db/schemas/mysql/schema.rs @@ -169,6 +169,7 @@ table! { user_uuid -> Text, device_uuid -> Text, device_name -> Text, + device_type -> Integer, login_time -> Timestamp, ip_address -> Text, } diff --git a/src/db/schemas/postgresql/schema.rs b/src/db/schemas/postgresql/schema.rs index 10b5313e..d1ea4b02 100644 --- a/src/db/schemas/postgresql/schema.rs +++ b/src/db/schemas/postgresql/schema.rs @@ -169,6 +169,7 @@ table! { user_uuid -> Text, device_uuid -> Text, device_name -> Text, + device_type -> Integer, login_time -> Timestamp, ip_address -> Text, } diff --git a/src/db/schemas/sqlite/schema.rs b/src/db/schemas/sqlite/schema.rs index 10b5313e..d1ea4b02 100644 --- a/src/db/schemas/sqlite/schema.rs +++ b/src/db/schemas/sqlite/schema.rs @@ -169,6 +169,7 @@ table! { user_uuid -> Text, device_uuid -> Text, device_name -> Text, + device_type -> Integer, login_time -> Timestamp, ip_address -> Text, } diff --git a/src/mail.rs b/src/mail.rs index 53e6b31f..b33efd95 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -17,7 +17,7 @@ use crate::{ encode_jwt, generate_delete_claims, generate_emergency_access_invite_claims, generate_invite_claims, generate_verify_email_claims, }, - db::models::User, + db::models::{Device, DeviceType, User}, error::Error, CONFIG, }; @@ -442,9 +442,8 @@ pub async fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult send_email(address, &subject, body_html, body_text).await } -pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, device: &str) -> EmptyResult { +pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, device: &Device) -> EmptyResult { use crate::util::upcase_first; - let device = upcase_first(device); let fmt = "%A, %B %_d, %Y at %r %Z"; let (subject, body_html, body_text) = get_text( @@ -453,7 +452,8 @@ pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTi "url": CONFIG.domain(), "img_src": CONFIG._smtp_img_src(), "ip": ip, - "device": device, + "device_name": upcase_first(&device.name), + "device_type": DeviceType::from_i32(device.atype).to_string(), "datetime": crate::util::format_naive_datetime_local(dt, fmt), }), )?; @@ -461,9 +461,14 @@ pub async fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTi send_email(address, &subject, body_html, body_text).await } -pub async fn send_incomplete_2fa_login(address: &str, ip: &str, dt: &NaiveDateTime, device: &str) -> EmptyResult { +pub async fn send_incomplete_2fa_login( + address: &str, + ip: &str, + dt: &NaiveDateTime, + device_name: &str, + device_type: &str, +) -> EmptyResult { use crate::util::upcase_first; - let device = upcase_first(device); let fmt = "%A, %B %_d, %Y at %r %Z"; let (subject, body_html, body_text) = get_text( @@ -472,7 +477,8 @@ pub async fn send_incomplete_2fa_login(address: &str, ip: &str, dt: &NaiveDateTi "url": CONFIG.domain(), "img_src": CONFIG._smtp_img_src(), "ip": ip, - "device": device, + "device_name": upcase_first(device_name), + "device_type": device_type, "datetime": crate::util::format_naive_datetime_local(dt, fmt), "time_limit": CONFIG.incomplete_2fa_time_limit(), }), diff --git a/src/static/templates/email/incomplete_2fa_login.hbs b/src/static/templates/email/incomplete_2fa_login.hbs index d9ff3950..a7120141 100644 --- a/src/static/templates/email/incomplete_2fa_login.hbs +++ b/src/static/templates/email/incomplete_2fa_login.hbs @@ -1,10 +1,11 @@ -Incomplete Two-Step Login From {{{device}}} +Incomplete Two-Step Login From {{{device_name}}} <!----------------> Someone attempted to log into your account with the correct master password, but did not provide the correct token or action required to complete the two-step login process within {{time_limit}} minutes of the initial login attempt. * Date: {{datetime}} * IP Address: {{ip}} -* Device Type: {{device}} +* Device Name: {{device_name}} +* Device Type: {{device_type}} If this was not you or someone you authorized, then you should change your master password as soon as possible, as it is likely to be compromised. {{> email/email_footer_text }} diff --git a/src/static/templates/email/incomplete_2fa_login.html.hbs b/src/static/templates/email/incomplete_2fa_login.html.hbs index 8bc1ce21..d388a1df 100644 --- a/src/static/templates/email/incomplete_2fa_login.html.hbs +++ b/src/static/templates/email/incomplete_2fa_login.html.hbs @@ -1,4 +1,4 @@ -Incomplete Two-Step Login From {{{device}}} +Incomplete Two-Step Login From {{{device_name}}} <!----------------> {{> email/email_header }} <table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> @@ -19,7 +19,12 @@ Incomplete Two-Step Login From {{{device}}} </tr> <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> <td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> - <b>Device Type:</b> {{device}} + <b>Device Name:</b> {{device_name}} + </td> + </tr> + <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> + <td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> + <b>Device Type:</b> {{device_type}} </td> </tr> <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> diff --git a/src/static/templates/email/new_device_logged_in.hbs b/src/static/templates/email/new_device_logged_in.hbs index 9734dcbe..b76b1d25 100644 --- a/src/static/templates/email/new_device_logged_in.hbs +++ b/src/static/templates/email/new_device_logged_in.hbs @@ -1,10 +1,11 @@ -New Device Logged In From {{{device}}} +New Device Logged In From {{{device_name}}} <!----------------> Your account was just logged into from a new device. * Date: {{datetime}} * IP Address: {{ip}} -* Device Type: {{device}} +* Device Name: {{device_name}} +* Device Type: {{device_type}} You can deauthorize all devices that have access to your account from the web vault ( {{url}} ) under Settings > My Account > Deauthorize Sessions. -{{> email/email_footer_text }}
\ No newline at end of file +{{> email/email_footer_text }} diff --git a/src/static/templates/email/new_device_logged_in.html.hbs b/src/static/templates/email/new_device_logged_in.html.hbs index 763e7994..52365acf 100644 --- a/src/static/templates/email/new_device_logged_in.html.hbs +++ b/src/static/templates/email/new_device_logged_in.html.hbs @@ -1,4 +1,4 @@ -New Device Logged In From {{{device}}} +New Device Logged In From {{{device_name}}} <!----------------> {{> email/email_header }} <table width="100%" cellpadding="0" cellspacing="0" style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> @@ -19,7 +19,12 @@ New Device Logged In From {{{device}}} </tr> <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> <td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> - <b>Device Type:</b> {{device}} + <b>Device Name:</b> {{device_name}} + </td> + </tr> + <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> + <td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;" valign="top"> + <b>Device Type:</b> {{device_type}} </td> </tr> <tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;"> |