Skip to content
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/defguard_core/src/handlers/openid_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use defguard_common::db::{
oauth2client::OAuth2Client,
},
};
use defguard_mail::templates::new_device_ocid_login_mail;
use defguard_mail::templates::new_device_oidc_login_mail;
use openidconnect::{
AccessToken, AdditionalClaims, Audience, AuthUrl, AuthorizationCode,
EmptyAdditionalProviderMetadata, EmptyExtraTokenFields, EndUserEmail, EndUserFamilyName,
Expand Down Expand Up @@ -580,7 +580,7 @@ pub async fn secure_authorization(
app.save(&appstate.pool).await?;

let mut conn = appstate.pool.begin().await?;
new_device_ocid_login_mail(
new_device_oidc_login_mail(
&session_info.user.email,
&mut conn,
Some(&session_info.session.into()),
Expand Down
24 changes: 9 additions & 15 deletions crates/defguard_gateway_manager/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,18 @@ use tokio::{
time::sleep,
};
use tokio_stream::wrappers::UnboundedReceiverStream;
use tonic::{Code, Status, transport::Endpoint};
use tonic::{
Code, Status,
transport::{Channel, Endpoint},
};

use crate::{Client, TEN_SECS, error::GatewayError};

#[cfg(test)]
use crate::GatewayManagerTestSupport;

#[cfg(test)]
#[derive(Debug, Default)]
#[derive(Default)]
struct GatewayTestTransport {
socket_path: Option<PathBuf>,
}
Expand Down Expand Up @@ -129,17 +132,11 @@ impl GatewayHandler {
}

#[cfg(not(test))]
fn connect_channel(
&self,
endpoint: Endpoint,
) -> Result<tonic::transport::Channel, GatewayError> {
fn connect_channel(&self, endpoint: &Endpoint) -> Result<Channel, GatewayError> {
self.connect_tls_channel(endpoint)
}

fn connect_tls_channel(
&self,
endpoint: Endpoint,
) -> Result<tonic::transport::Channel, GatewayError> {
fn connect_tls_channel(&self, endpoint: &Endpoint) -> Result<Channel, GatewayError> {
let settings = Settings::get_current_settings();
let Some(ca_cert_der) = settings.ca_cert_der else {
return Err(GatewayError::EndpointError(
Expand Down Expand Up @@ -371,7 +368,7 @@ impl GatewayHandler {
let endpoint = self.endpoint()?;
let uri = endpoint.uri().to_string();

let channel = self.connect_channel(endpoint)?;
let channel = self.connect_channel(&endpoint)?;

debug!("Connecting to Gateway {uri}");
let interceptor = ClientVersionInterceptor::new(
Expand Down Expand Up @@ -553,10 +550,7 @@ impl GatewayHandler {
.map_or(TEN_SECS, GatewayManagerTestSupport::handler_reconnect_delay)
}

fn connect_channel(
&self,
endpoint: Endpoint,
) -> Result<tonic::transport::Channel, GatewayError> {
fn connect_channel(&self, endpoint: &Endpoint) -> Result<Channel, GatewayError> {
if let Some(socket_path) = self.test_transport.socket_path().cloned() {
return Ok(endpoint.connect_with_connector_lazy(tower::service_fn(
move |_: tonic::transport::Uri| {
Expand Down
60 changes: 44 additions & 16 deletions crates/defguard_mail/src/mail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,15 @@ pub enum MailError {
InvalidPort(i32),
}

/// Mail message
#[derive(Debug)]
pub struct Mail {
pub(crate) to: String,
pub(crate) subject: String,
content: String,
// HTML version of the message.
html: String,
// Plain text version of the message.
text: String,
context: Context,
attachments: Vec<Attachment>, // text/plain
images: Vec<(String, Vec<u8>)>, // image/png
Expand All @@ -92,7 +96,7 @@ pub struct Mail {
impl Mail {
/// Create new [`Mail`].
#[must_use]
pub fn new<T>(to: T, subject: String, content: String) -> Mail
pub fn new<T>(to: T, subject: String, html: String, text: String) -> Mail
where
T: Into<String>,
{
Expand All @@ -107,7 +111,8 @@ impl Mail {
Self {
to: to.into(),
subject,
content,
html,
text,
context: Context::new(),
attachments: Vec::new(),
images,
Expand All @@ -126,12 +131,6 @@ impl Mail {
&self.subject
}

/// Getter for `content`.
#[must_use]
pub fn content(&self) -> &str {
&self.content
}

/// Add to context.
pub fn add_to_context<K, V>(&mut self, key: K, value: &V)
where
Expand Down Expand Up @@ -171,8 +170,8 @@ impl Mail {
.to(Mailbox::from_str(&self.to)?)
.subject(self.subject);

let plain = SinglePart::plain("PLAIN IS NOT AVAILABLE AT THE MOMENT.".to_string());
let html = SinglePart::html(self.content);
let plain = SinglePart::plain(self.text);
let html = SinglePart::html(self.html);
let image_png = "image/png".parse::<ContentType>().unwrap();
let mut related = MultiPart::related().singlepart(html);
for (name, bytes) in self.images {
Expand Down Expand Up @@ -284,7 +283,7 @@ pub enum MailMessage {
NewAccount,
NewDevice,
NewDeviceLogin,
NewDeviceOCIDLogin,
NewDeviceOIDCLogin,
/// Gateway has disconnected.
GatewayDisconnect,
/// Gateway has reconnected.
Expand Down Expand Up @@ -319,7 +318,7 @@ impl MailMessage {
Self::NewAccount => "Defguard: User enrollment",
Self::NewDevice => "Defguard: new device added to your account",
Self::NewDeviceLogin => "Defguard: New device logged in to your account",
Self::NewDeviceOCIDLogin => "New login to OCID application",
Self::NewDeviceOIDCLogin => "New login to OIDC application",
Self::GatewayDisconnect => "Defguard: Gateway disconnected",
Self::GatewayReconnect => "Defguard: Gateway reconnected",
Self::MFAActivation => "Multi-Factor Authentication activation",
Expand All @@ -342,7 +341,7 @@ impl MailMessage {
Self::NewAccount => "new-account",
Self::NewDevice => "new-device",
Self::NewDeviceLogin => "new-device-login",
Self::NewDeviceOCIDLogin => "new-device-ocid-login",
Self::NewDeviceOIDCLogin => "new-device-oidc-login",
Self::GatewayDisconnect => "gateway-disconnect",
Self::GatewayReconnect => "gateway-reconnect",
Self::MFAActivation => "mfa-activation",
Expand All @@ -364,7 +363,7 @@ impl MailMessage {
Self::NewAccount => include_str!("../templates/new-account.mjml"),
Self::NewDevice => include_str!("../templates/new-device.mjml"),
Self::NewDeviceLogin => include_str!("../templates/new-device-login.mjml"),
Self::NewDeviceOCIDLogin => include_str!("../templates/new-device-ocid-login.mjml"),
Self::NewDeviceOIDCLogin => include_str!("../templates/new-device-oidc-login.mjml"),
Self::GatewayDisconnect => include_str!("../templates/gateway-disconnected.mjml"),
Self::GatewayReconnect => include_str!("../templates/gateway-reconnected.mjml"),
Self::MFAActivation => include_str!("../templates/mfa-activation.mjml"),
Expand All @@ -379,6 +378,30 @@ impl MailMessage {
}
}

pub(crate) const fn text_template(&self) -> &str {
match self {
Self::Test => include_str!("../templates/test.text"),
Self::Welcome => include_str!("../templates/enrollment-welcome.text"),
Self::SupportData => include_str!("../templates/support-data.text"),
Self::DesktopStart => include_str!("../templates/desktop-start.text"),
Self::NewAccount => include_str!("../templates/new-account.text"),
Self::NewDevice => include_str!("../templates/new-device.text"),
Self::NewDeviceLogin => include_str!("../templates/new-device-login.text"),
Self::NewDeviceOIDCLogin => include_str!("../templates/new-device-oidc-login.text"),
Self::GatewayDisconnect => include_str!("../templates/gateway-disconnected.text"),
Self::GatewayReconnect => include_str!("../templates/gateway-reconnected.text"),
Self::MFAActivation => include_str!("../templates/mfa-activation.text"),
Self::MFAConfigured => include_str!("../templates/mfa-configured.text"),
Self::MFACode => include_str!("../templates/mfa-code.text"),
Self::PasswordReset => include_str!("../templates/password-reset.text"),
Self::PasswordResetDone => include_str!("../templates/password-reset-done.text"),
Self::UserImportBlocked => include_str!("../templates/plain-notification.text"),
Self::EnrollmentNotification => {
include_str!("../templates/enrollment-admin-notification.text")
}
}
}

/// Fill `Context` from database.
pub(crate) async fn fill_context(
&self,
Expand All @@ -401,13 +424,18 @@ impl MailMessage {
context: &Context,
to: &str,
) -> Result<Mail, TemplateError> {
// Build HTML message.
tera.add_raw_template(self.template_name(), self.mjml_template())?;
let processed = tera.render(self.template_name(), context)?;
let parsed = mrml::parse(processed)?;
let opts = mrml::prelude::render::RenderOptions::default();
let html = parsed.element.render(&opts)?;

let mut mail = Mail::new(to, self.subject(), html);
// Build plain text message.
tera.add_raw_template(self.template_name(), self.text_template())?;
let text = tera.render(self.template_name(), context)?;

let mut mail = Mail::new(to, self.subject(), html, text);
// Add PNG images.
match self {
Self::NewAccount => {
Expand Down
4 changes: 2 additions & 2 deletions crates/defguard_mail/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ pub async fn new_device_login_mail(
}

/// New device login from OpenID Connect.
pub async fn new_device_ocid_login_mail(
pub async fn new_device_oidc_login_mail(
to: &str,
conn: &mut PgConnection,
session: Option<&SessionContext>,
Expand All @@ -334,7 +334,7 @@ pub async fn new_device_ocid_login_mail(
context.insert("oauth2client_name", &oauth2client_name);
context.insert("profile_url", &url);

let message = MailMessage::NewDeviceOCIDLogin;
let message = MailMessage::NewDeviceOIDCLogin;
message.fill_context(conn, &mut context).await?;
message.mail(&mut tera, &context, to)?.send_and_forget();

Expand Down
4 changes: 2 additions & 2 deletions crates/defguard_mail/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,13 @@ fn send_new_device_login_mail(_: PgPoolOptions, options: PgConnectOptions) {

#[ignore = "requires SMTP server"]
#[sqlx::test]
fn send_new_device_ocid_login_mail(_: PgPoolOptions, options: PgConnectOptions) {
fn send_new_device_oidc_login_mail(_: PgPoolOptions, options: PgConnectOptions) {
let pool = setup_pool(options).await;
set_smtp_settings(&pool).await;

let mut conn = pool.begin().await.unwrap();
let client_name = "RemoteApp";
templates::new_device_ocid_login_mail(
templates::new_device_oidc_login_mail(
&env::var("SMTP_TO").unwrap(),
&mut conn,
None,
Expand Down
26 changes: 13 additions & 13 deletions crates/defguard_mail/templates/base.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<mj-font name="Geist" href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&family=IBM+Plex+Mono&display=swaphttps://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" />
<mj-attributes>
<mj-all padding="0" />
<mj-class name="t-title-h1" font-size="32px" font-weight="600" line-height="44px" font-family="Geist" color="inherit" />
<mj-class name="t-title-h3" font-size="24px" font-weight="600" line-height="32px" font-family="Geist" color="inherit" />
<mj-class name="t-body-primary-600" font-size="16px" font-weight="600" line-height="24px" font-family="Geist" color="inherit" />
<mj-class name="t-body-primary-400" font-size="16px" font-weight="400" line-height="24px" font-family="Geist" color="inherit" />
<mj-class name="t-body-sm-400" font-size="14px" font-weight="400" line-height="20px" font-family="Geist" color="inherit" />
<mj-class name="t-body-sm-500" font-size="14px" font-weight="500" line-height="20px" font-family="Geist" color="inherit" />
<mj-class name="t-body-xs-400" font-size="12px" font-weight="400" line-height="16px" font-family="Geist" color="inherit" />
<mj-class name="t-body-xs-500" font-size="12px" font-weight="500" line-height="16px" font-family="Geist" color="inherit" />
<mj-class name="t-title-h1" font-size="32px" font-weight="600" line-height="44px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-title-h3" font-size="24px" font-weight="600" line-height="32px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-primary-600" font-size="16px" font-weight="600" line-height="24px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-primary-400" font-size="16px" font-weight="400" line-height="24px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-sm-400" font-size="14px" font-weight="400" line-height="20px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-sm-500" font-size="14px" font-weight="500" line-height="20px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-xs-400" font-size="12px" font-weight="400" line-height="16px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="t-body-xs-500" font-size="12px" font-weight="500" line-height="16px" font-family="Geist, Arial, sans-serif" color="inherit" />
<mj-class name="divider" border-width="1px" border-style="solid" border-color="#DFE3E9" />
<mj-class name="s-5xl" padding="0px 0px 48px 0px" />
<mj-class name="s-4xl" padding="0px 0px 40px 0px" />
Expand All @@ -21,8 +21,8 @@
<mj-class name="s-md" padding="0px 0px 12px 0px" />
<mj-class name="s-sm" padding="0px 0px 8px 0px" />
<mj-class name="s-xs" padding="0px 0px 4px 0px" />
<mj-class name="dg-button" border="1px solid #3961DB" padding="0" margin="0" inner-padding="13px 15px" background-color="#3961DB" border-radius="8px" color="#ffffff" font-family="Geist" font-size="14px" font-weight="400" />
<mj-class name="dg-button-outlined" border="1px solid #3961DB" padding="0" margin="0" inner-padding="13px 15px" background-color="#fff" border-radius="8px" color="#3961DB" font-family="Geist" font-size="14px" font-weight="400" />
<mj-class name="dg-button" border="1px solid #3961DB" padding="0" margin="0" inner-padding="13px 15px" background-color="#3961DB" border-radius="8px" color="#ffffff" font-family="Geist, Arial, sans-serif" font-size="14px" font-weight="400" />
<mj-class name="dg-button-outlined" border="1px solid #3961DB" padding="0" margin="0" inner-padding="13px 15px" background-color="#fff" border-radius="8px" color="#3961DB" font-family="Geist, Arial, sans-serif" font-size="14px" font-weight="400" />
{% block attributes %}
{% endblock attributes %}
</mj-attributes>
Expand All @@ -33,7 +33,7 @@
}

.t-titles-h1 {
font-family: Geist;
font-family: Geist, Arial, sans-serif;
font-size: 32px;
line-height: 44px;
font-weight: 600;
Expand Down Expand Up @@ -147,14 +147,14 @@
}

.dg-table td:not(:first-child) {
font-family: Geist;
font-family: Geist, Arial, sans-serif;
font-size: 14px;
font-weight: 500;
color: #141517;
}

.dg-table td:first-child {
font-family: Geist;
font-family: Geist, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
font-weight: 400;
Expand Down
2 changes: 1 addition & 1 deletion crates/defguard_mail/templates/desktop-start.text
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ title }}
{{ subtitle }}
{% if subtitle %}{{ subtitle }}{% endif %}

{{ label_url }}: {{ url }}
{{ label_token }}: {{ token }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{ title }}
{% if subtitle %}{{ subtitle }}{% endif %}

{{ user_name }} {{ message }}
{{ goodday }}
2 changes: 2 additions & 0 deletions crates/defguard_mail/templates/enrollment-welcome.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@
</mj-column>
</mj-section>

{{ macros::footer_divider() }}

{% endblock content %}
1 change: 1 addition & 0 deletions crates/defguard_mail/templates/enrollment-welcome.text
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ welcome_message_content }}
6 changes: 6 additions & 0 deletions crates/defguard_mail/templates/gateway-disconnected.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{ title }}
{% if subtitle %}{{ subtitle }}{% endif %}

{{ gateway_label }}: {{ gateway_name }}
{{ ip_address_label }}: {{ ip_address }}
{{ location_label }}: {{ location_name }}
6 changes: 6 additions & 0 deletions crates/defguard_mail/templates/gateway-reconnected.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{{ title }}
{% if subtitle %}{{ subtitle }}{% endif %}

{{ gateway_label }}: {{ gateway_name }}
{{ ip_address_label }}: {{ ip_address }}
{{ location_label }}: {{ location_name }}
2 changes: 1 addition & 1 deletion crates/defguard_mail/templates/macros.mjml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

{% macro action_link(text, href, css_class="", mj_class="") %}
<mj-text css-class="{{ css_class }}" mj-class="{{ mj_class }}">
<a mj-class="t-body-sm-400" css-class="fg-action-c link" href="{{ href }}" style="color: #3961DB; text-decoration: none; font-family: Geist; font-weight: 400; font-size: 14px;">
<a mj-class="t-body-sm-400" css-class="fg-action-c link" href="{{ href }}" style="color: #3961DB; text-decoration: none; font-family: Geist, Arial, sans-serif; font-weight: 400; font-size: 14px;">
{{ text }}
</a>
</mj-text>
Expand Down
7 changes: 7 additions & 0 deletions crates/defguard_mail/templates/mfa-activation.text
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{ title }}
{% if subtitle %}{{ subtitle }}{% endif %}

{{ code }}

{{ code_is_valid }} {{ timeout }}
{{ datetime }}
Loading
Loading