Skip to content

Commit

Permalink
feat!: [#591] extract new types for SMTP server configuration
Browse files Browse the repository at this point in the history
From:

```toml
[mail]
email_verification_enabled = false
from = "[email protected]"
password = ""
port = 25
reply_to = "[email protected]"
server = ""
username = ""
```

To:

```toml
[mail]
email_verification_enabled = false
from = "[email protected]"
reply_to = "[email protected]"

[mail.smtp]
port = 25
server = ""

[mail.smtp.credentials]
password = ""
username = ""
```
  • Loading branch information
josecelano committed Jun 13, 2024
1 parent cd8248a commit 1bc00ce
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 68 deletions.
3 changes: 2 additions & 1 deletion share/default/config/index.container.mysql.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ log_level = "info"
[database]
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"

2 changes: 1 addition & 1 deletion share/default/config/index.container.sqlite3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ log_level = "info"
[database]
connect_url = "sqlite:///var/lib/torrust/index/database/sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ url = "http://tracker:7070"
[database]
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
2 changes: 1 addition & 1 deletion share/default/config/index.public.e2e.container.mysql.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ url = "udp://tracker:6969"
[database]
connect_url = "mysql://root:root_secret_password@mysql:3306/torrust_index_e2e_testing"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ url = "udp://tracker:6969"
[database]
connect_url = "sqlite:///var/lib/torrust/index/database/e2e_testing_sqlite3.db?mode=rwc"

[mail]
[mail.smtp]
port = 1025
server = "mailcatcher"
2 changes: 1 addition & 1 deletion src/bootstrap/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::sync::Once;
use tracing::info;
use tracing::level_filters::LevelFilter;

use crate::config::v1::logging::LogLevel;
use crate::config::LogLevel;

static INIT: Once = Once::new();

Expand Down
33 changes: 25 additions & 8 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,33 @@ use url::Url;
use crate::web::api::server::DynError;

pub type Settings = v1::Settings;

pub type Api = v1::api::Api;

pub type Auth = v1::auth::Auth;
pub type EmailOnSignup = v1::auth::EmailOnSignup;
pub type SecretKey = v1::auth::SecretKey;
pub type PasswordConstraints = v1::auth::PasswordConstraints;

pub type Database = v1::database::Database;

pub type ImageCache = v1::image_cache::ImageCache;

pub type Mail = v1::mail::Mail;
pub type Smtp = v1::mail::Smtp;
pub type Credentials = v1::mail::Credentials;

pub type Network = v1::net::Network;

pub type TrackerStatisticsImporter = v1::tracker_statistics_importer::TrackerStatisticsImporter;

pub type Tracker = v1::tracker::Tracker;
pub type ApiToken = v1::tracker::ApiToken;

pub type Logging = v1::logging::Logging;
pub type LogLevel = v1::logging::LogLevel;

pub type Website = v1::website::Website;
pub type EmailOnSignup = v1::auth::EmailOnSignup;
pub type PasswordConstraints = v1::auth::PasswordConstraints;

/// Prefix for env vars that overwrite configuration options.
const CONFIG_OVERRIDE_PREFIX: &str = "TORRUST_INDEX_CONFIG_OVERRIDE_";
Expand Down Expand Up @@ -321,9 +336,7 @@ mod tests {

use url::Url;

use crate::config::v1::auth::SecretKey;
use crate::config::v1::tracker::ApiToken;
use crate::config::{Configuration, ConfigurationPublic, Info, Settings};
use crate::config::{ApiToken, Configuration, ConfigurationPublic, Info, SecretKey, Settings};

#[cfg(test)]
fn default_config_toml() -> String {
Expand Down Expand Up @@ -358,10 +371,14 @@ mod tests {
email_verification_enabled = false
from = "[email protected]"
reply_to = "[email protected]"
username = ""
password = ""
server = ""
[mail.smtp]
port = 25
server = ""
[mail.smtp.credentials]
password = ""
username = ""
[image_cache]
max_request_timeout_ms = 1000
Expand Down
84 changes: 64 additions & 20 deletions src/config/v1/mail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,9 @@ pub struct Mail {
/// The email address to reply to.
#[serde(default = "Mail::default_reply_to")]
pub reply_to: Mailbox,
/// The username to use for SMTP authentication.
#[serde(default = "Mail::default_username")]
pub username: String,
/// The password to use for SMTP authentication.
#[serde(default = "Mail::default_password")]
pub password: String,
/// The SMTP server to use.
#[serde(default = "Mail::default_server")]
pub server: String,
/// The SMTP port to use.
#[serde(default = "Mail::default_port")]
pub port: u16,
/// The SMTP server configuration.
#[serde(default = "Mail::default_smtp")]
pub smtp: Smtp,
}

impl Default for Mail {
Expand All @@ -33,10 +24,7 @@ impl Default for Mail {
email_verification_enabled: Self::default_email_verification_enabled(),
from: Self::default_from(),
reply_to: Self::default_reply_to(),
username: Self::default_username(),
password: Self::default_password(),
server: Self::default_server(),
port: Self::default_port(),
smtp: Self::default_smtp(),
}
}
}
Expand All @@ -54,19 +42,75 @@ impl Mail {
"[email protected]".parse().expect("valid mailbox")
}

fn default_username() -> String {
String::default()
fn default_smtp() -> Smtp {
Smtp::default()
}
}

fn default_password() -> String {
String::default()
/// SMTP configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Smtp {
/// The SMTP port to use.
#[serde(default = "Smtp::default_port")]
pub port: u16,
/// The SMTP server to use.
#[serde(default = "Smtp::default_server")]
pub server: String,
/// The SMTP server credentials.
#[serde(default = "Smtp::default_credentials")]
pub credentials: Credentials,
}

impl Default for Smtp {
fn default() -> Self {
Self {
server: Self::default_server(),
port: Self::default_port(),
credentials: Self::default_credentials(),
}
}
}

impl Smtp {
fn default_server() -> String {
String::default()
}

fn default_port() -> u16 {
25
}

fn default_credentials() -> Credentials {
Credentials::default()
}
}

/// SMTP configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Credentials {
/// The password to use for SMTP authentication.
#[serde(default = "Credentials::default_password")]
pub password: String,
/// The username to use for SMTP authentication.
#[serde(default = "Credentials::default_username")]
pub username: String,
}

impl Default for Credentials {
fn default() -> Self {
Self {
username: Self::default_username(),
password: Self::default_password(),
}
}
}

impl Credentials {
fn default_username() -> String {
String::default()
}

fn default_password() -> String {
String::default()
}
}
2 changes: 1 addition & 1 deletion src/config/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl Settings {
if let Some(_password) = self.database.connect_url.password() {
let _ = self.database.connect_url.set_password(Some("***"));
}
"***".clone_into(&mut self.mail.password);
"***".clone_into(&mut self.mail.smtp.credentials.password);
self.auth.secret_key = SecretKey::new("***");
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,14 @@
//! email_verification_enabled = false
//! from = "[email protected]"
//! reply_to = "[email protected]"
//! username = ""
//! password = ""
//! server = ""
//!
//! [mail.smtp]
//! port = 25
//! server = ""
//!
//! [mail.smtp.credentials]
//! password = ""
//! username = ""
//!
//! [image_cache]
//! max_request_timeout_ms = 1000
Expand Down
15 changes: 9 additions & 6 deletions src/mailer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,22 @@ impl Service {
async fn get_mailer(cfg: &Configuration) -> Mailer {
let settings = cfg.settings.read().await;

if !settings.mail.username.is_empty() && !settings.mail.password.is_empty() {
if !settings.mail.smtp.credentials.username.is_empty() && !settings.mail.smtp.credentials.password.is_empty() {
// SMTP authentication
let creds = Credentials::new(settings.mail.username.clone(), settings.mail.password.clone());
let creds = Credentials::new(
settings.mail.smtp.credentials.username.clone(),
settings.mail.smtp.credentials.password.clone(),
);

AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.server)
.port(settings.mail.port)
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.smtp.server)
.port(settings.mail.smtp.port)
.credentials(creds)
.authentication(vec![Mechanism::Login, Mechanism::Xoauth2, Mechanism::Plain])
.build()
} else {
// SMTP without authentication
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.server)
.port(settings.mail.port)
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&settings.mail.smtp.server)
.port(settings.mail.smtp.port)
.build()
}
}
Expand Down
45 changes: 36 additions & 9 deletions src/web/api/client/v1/contexts/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ use url::Url;

use crate::config::v1::tracker::ApiToken;
use crate::config::{
Api as DomainApi, Auth as DomainAuth, Database as DomainDatabase, ImageCache as DomainImageCache, Mail as DomainMail,
Network as DomainNetwork, PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings,
Tracker as DomainTracker, TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
Api as DomainApi, Auth as DomainAuth, Credentials as DomainCredentials, Database as DomainDatabase,
ImageCache as DomainImageCache, Mail as DomainMail, Network as DomainNetwork,
PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings, Smtp as DomainSmtp, Tracker as DomainTracker,
TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
};

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
Expand Down Expand Up @@ -66,10 +67,20 @@ pub struct Mail {
pub email_verification_enabled: bool,
pub from: String,
pub reply_to: String,
pub username: String,
pub password: String,
pub smtp: Smtp,
}

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct Smtp {
pub server: String,
pub port: u16,
pub credentials: Credentials,
}

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
pub struct Credentials {
pub username: String,
pub password: String,
}

#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
Expand Down Expand Up @@ -169,10 +180,26 @@ impl From<DomainMail> for Mail {
email_verification_enabled: mail.email_verification_enabled,
from: mail.from.to_string(),
reply_to: mail.reply_to.to_string(),
username: mail.username,
password: mail.password,
server: mail.server,
port: mail.port,
smtp: Smtp::from(mail.smtp),
}
}
}

impl From<DomainSmtp> for Smtp {
fn from(smtp: DomainSmtp) -> Self {
Self {
server: smtp.server,
port: smtp.port,
credentials: Credentials::from(smtp.credentials),
}
}
}

impl From<DomainCredentials> for Credentials {
fn from(credentials: DomainCredentials) -> Self {
Self {
username: credentials.username,
password: credentials.password,
}
}
}
Expand Down
Loading

0 comments on commit 1bc00ce

Please sign in to comment.