From bc6a53b847a398191f221d8d98829d50b2505e53 Mon Sep 17 00:00:00 2001 From: vpl Date: Mon, 22 Jul 2019 08:26:24 +0200 Subject: [PATCH] Add new device email when user logs in --- src/api/identity.rs | 18 ++- src/config.rs | 1 + src/mail.rs | 27 +++- .../templates/email/new_device_logged_in.hbs | 14 ++ .../email/new_device_logged_in.html.hbs | 144 ++++++++++++++++++ 5 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 src/static/templates/email/new_device_logged_in.hbs create mode 100644 src/static/templates/email/new_device_logged_in.html.hbs diff --git a/src/api/identity.rs b/src/api/identity.rs index 3602dc97..74e9fa37 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -15,6 +15,8 @@ use crate::api::{ApiResult, EmptyResult, JsonResult}; use crate::auth::ClientIp; +use crate::mail; + use crate::CONFIG; pub fn routes() -> Vec { @@ -104,20 +106,34 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: ClientIp) -> JsonResult let device_id = data.device_identifier.clone().expect("No device id provided"); let device_name = data.device_name.clone().expect("No device name provided"); + let mut send_email = false; // Find device or create new let mut device = match Device::find_by_uuid(&device_id, &conn) { Some(device) => { // Check if owned device, and recreate if not if device.user_uuid != user.uuid { info!("Device exists but is owned by another user. The old device will be discarded"); + send_email = true; Device::new(device_id, user.uuid.clone(), device_name, device_type) } else { device } } - None => Device::new(device_id, user.uuid.clone(), device_name, device_type), + None => { + send_email = true; + Device::new(device_id, user.uuid.clone(), device_name, device_type) + } }; + if CONFIG.mail_enabled() && send_email { + mail::send_new_device_logged_in( + &username, + &ip.ip.to_string(), + &device.updated_at, + &device.name, + )?; + } + let twofactor_token = twofactor_auth(&user.uuid, &data, &mut device, &conn)?; // Common diff --git a/src/config.rs b/src/config.rs index 91feb1c6..85d97511 100644 --- a/src/config.rs +++ b/src/config.rs @@ -526,6 +526,7 @@ fn load_templates(path: &str) -> Handlebars { // First register default templates here reg!("email/invite_accepted", ".html"); reg!("email/invite_confirmed", ".html"); + reg!("email/new_device_logged_in", ".html"); reg!("email/pw_hint_none", ".html"); reg!("email/pw_hint_some", ".html"); reg!("email/send_org_invite", ".html"); diff --git a/src/mail.rs b/src/mail.rs index 2b814b54..576cb6b0 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -3,13 +3,14 @@ use lettre::smtp::ConnectionReuseParameters; use lettre::{ClientSecurity, ClientTlsParameters, SmtpClient, SmtpTransport, Transport}; use lettre_email::{EmailBuilder, MimeMultipartType, PartBuilder}; use native_tls::{Protocol, TlsConnector}; -use quoted_printable::encode_to_str; use percent_encoding::{percent_encode, DEFAULT_ENCODE_SET}; +use quoted_printable::encode_to_str; use crate::api::EmptyResult; use crate::auth::{encode_jwt, generate_invite_claims}; use crate::error::Error; use crate::CONFIG; +use chrono::NaiveDateTime; fn mailer() -> SmtpTransport { let host = CONFIG.smtp_host().unwrap(); @@ -136,6 +137,30 @@ pub fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult { send_email(&address, &subject, &body_html, &body_text) } +pub fn send_new_device_logged_in( + address: &str, + ip: &str, + dt: &NaiveDateTime, + device: &str, +) -> EmptyResult { + use crate::util::upcase_first; + let device = upcase_first(device); + + let datetime = dt.format("%A, %B %_d, %Y at %H:%M").to_string(); + + let (subject, body_html, body_text) = get_text( + "email/new_device_logged_in", + json!({ + "url": CONFIG.domain(), + "ip": ip, + "device": device, + "datetime": datetime, + }), + )?; + + send_email(&address, &subject, &body_html, &body_text) +} + fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) -> EmptyResult { let html = PartBuilder::new() .body(encode_to_str(body_html)) diff --git a/src/static/templates/email/new_device_logged_in.hbs b/src/static/templates/email/new_device_logged_in.hbs new file mode 100644 index 00000000..0ef4bb26 --- /dev/null +++ b/src/static/templates/email/new_device_logged_in.hbs @@ -0,0 +1,14 @@ +New Device Logged In From {{device}} + + +

+ Your account was just logged into from a new device. + + Date: {{datetime}} + IP Address: {{ip}} + Device Type: {{device}} + + You can deauthorize all devices that have access to your account from the + web vault under Settings > My Account > Deauthorize Sessions. +

+ diff --git a/src/static/templates/email/new_device_logged_in.html.hbs b/src/static/templates/email/new_device_logged_in.html.hbs new file mode 100644 index 00000000..0bf1e7b8 --- /dev/null +++ b/src/static/templates/email/new_device_logged_in.html.hbs @@ -0,0 +1,144 @@ +New Device Logged In From {{device}} + + + + + + Bitwarden_rs + + + + + + + + + + +
+ + + + +
+ + + + +
+ + + + + + + + + + + + + + + + +
+ Your account was just logged into from a new device. +
+ Date: {{datetime}} +
+ IP Address: {{ip}} +
+ Device Type: {{device}} +
+ You can deauthorize all devices that have access to your account from the web vault under Settings > My Account > Deauthorize Sessions. +
+
+ + + + + +
+
+ +