Skip to content

Commit 39cab7f

Browse files
Dfg 425 password reset success mail (#475)
* Send password reset success mail
1 parent 892c325 commit 39cab7f

File tree

6 files changed

+84
-2
lines changed

6 files changed

+84
-2
lines changed

src/grpc/password_reset.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use crate::{
1010
models::enrollment::{Token, PASSWORD_RESET_TOKEN_TYPE},
1111
DbPool, User,
1212
},
13-
handlers::{mail::send_password_reset_email, user::check_password_strength},
13+
handlers::{
14+
mail::{send_password_reset_email, send_password_reset_success_email},
15+
user::check_password_strength,
16+
},
1417
ldap::utils::ldap_change_password,
1518
mail::Mail,
1619
};
@@ -207,6 +210,18 @@ impl password_reset_service_server::PasswordResetService for PasswordResetServer
207210
debug!("Starting password reset: {request:?}");
208211
let enrollment = self.validate_session(&request).await?;
209212

213+
let ip_address = request
214+
.metadata()
215+
.get("ip_address")
216+
.and_then(|value| value.to_str().map(ToString::to_string).ok())
217+
.unwrap_or_default();
218+
219+
let user_agent = request
220+
.metadata()
221+
.get("user_agent")
222+
.and_then(|value| value.to_str().map(ToString::to_string).ok())
223+
.unwrap_or_default();
224+
210225
let request = request.into_inner();
211226
if let Err(err) = check_password_strength(&request.password) {
212227
error!("Password not strong enough: {err}");
@@ -236,6 +251,13 @@ impl password_reset_service_server::PasswordResetService for PasswordResetServer
236251
Status::internal("unexpected error")
237252
})?;
238253

254+
send_password_reset_success_email(
255+
&user,
256+
&self.mail_tx,
257+
Some(ip_address),
258+
Some(user_agent),
259+
)?;
260+
239261
Ok(Response::new(()))
240262
}
241263
}

src/handlers/mail.rs

+31
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static EMAIL_MFA_CODE_EMAIL_SUBJECT: &str = "Your Multi-Factor Authentication Co
3939
static GATEWAY_DISCONNECTED: &str = "Defguard: Gateway disconnected";
4040

4141
pub static EMAIL_PASSOWRD_RESET_START_SUBJECT: &str = "Defguard: Password reset";
42+
pub static EMAIL_PASSOWRD_RESET_SUCCESS_SUBJECT: &str = "Defguard: Password reset success";
4243

4344
#[derive(Clone, Deserialize)]
4445
pub struct TestMail {
@@ -452,3 +453,33 @@ pub fn send_password_reset_email(
452453
}
453454
}
454455
}
456+
457+
pub fn send_password_reset_success_email(
458+
user: &User,
459+
mail_tx: &UnboundedSender<Mail>,
460+
ip_address: Option<String>,
461+
device_info: Option<String>,
462+
) -> Result<(), TokenError> {
463+
debug!("Sending password reset success email to {}", user.email);
464+
465+
let mail = Mail {
466+
to: user.email.clone(),
467+
subject: EMAIL_PASSOWRD_RESET_SUCCESS_SUBJECT.into(),
468+
content: templates::email_password_reset_success_mail(ip_address, device_info)?,
469+
attachments: Vec::new(),
470+
result_tx: None,
471+
};
472+
473+
let to = mail.to.clone();
474+
475+
match mail_tx.send(mail) {
476+
Ok(()) => {
477+
info!("Password reset email success sent to {to}");
478+
Ok(())
479+
}
480+
Err(err) => {
481+
error!("Failed to send password reset success email to {to} with error:\n{err}");
482+
Ok(())
483+
}
484+
}
485+
}

src/templates.rs

+13
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ static MAIL_EMAIL_MFA_ACTIVATION: &str =
2929
static MAIL_EMAIL_MFA_CODE: &str = include_str!("../templates/mail_email_mfa_code.tera");
3030
static MAIL_PASSWORD_RESET_START: &str =
3131
include_str!("../templates/mail_password_reset_start.tera");
32+
static MAIL_PASSWORD_RESET_SUCCESS: &str =
33+
include_str!("../templates/mail_password_reset_success.tera");
3234

3335
#[allow(dead_code)]
3436
static MAIL_DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:00Z";
@@ -295,6 +297,17 @@ pub fn email_password_reset_mail(
295297
Ok(tera.render("mail_passowrd_reset_start", &context)?)
296298
}
297299

300+
pub fn email_password_reset_success_mail(
301+
ip_address: Option<String>,
302+
device_info: Option<String>,
303+
) -> Result<String, TemplateError> {
304+
let (mut tera, context) = get_base_tera(None, None, ip_address, device_info)?;
305+
306+
tera.add_raw_template("mail_passowrd_reset_success", MAIL_PASSWORD_RESET_SUCCESS)?;
307+
308+
Ok(tera.render("mail_passowrd_reset_success", &context)?)
309+
}
310+
298311
#[cfg(test)]
299312
mod test {
300313
use crate::config::DefGuardConfig;

templates/base.tera

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@
215215
{% endblock %}
216216

217217
<!-- device info -->
218-
<div style="margin: 30px auto 0px; max-width: 600px;">
218+
<div style="margin: 0px auto 0px; max-width: 600px;">
219219
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%">
220220
<tbody>
221221
<tr>

templates/mail_password_reset_start.tera

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ macros::paragraph(content="Or click the button below:"),
3333
text-align: center;
3434
display: inline-block;
3535
margin: 0px auto;
36+
margin-bottom: 10px;
3637
cursor: pointer;
3738
"><span>Reset password</span></a></p>
3839
{% endblock %}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{# Requires context
2+
enrollment_url -> URL of the enrollment service
3+
link_url -> URL of the enrollment service with the token query param included
4+
defguard_url -> URL of defguard core Web UI
5+
token -> enrollment token
6+
#}
7+
{% extends "base.tera" %}
8+
{% import "macros.tera" as macros %}
9+
{% block mail_content %}
10+
{% set section_content = [
11+
macros::paragraph(content="<b>Password reset</b>"),
12+
macros::paragraph(content= "Your password has been successfully changed."),
13+
] %}
14+
{{ macros::text_section(content_array=section_content)}}
15+
{% endblock %}

0 commit comments

Comments
 (0)