From c877583979ab0bd76c135b1d88d58298bb9e3680 Mon Sep 17 00:00:00 2001
From: BlackDex <black.dex@gmail.com>
Date: Sat, 12 Sep 2020 21:47:24 +0200
Subject: [PATCH] Allow multiple SMTP Auth meganisms.

- Allow all SMTP Auth meganisms supported by Lettre.
- The config value order is leading and values can be separated by a
  comma ','
- Case doesn't matter, and invalid values are ignored.
- Warning is printed when no valid value is found at all.
---
 .env.template | 11 +++++++----
 src/config.rs |  4 ++--
 src/mail.rs   | 22 ++++++++++++++++------
 3 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/.env.template b/.env.template
index 61b960dc..06fd163e 100644
--- a/.env.template
+++ b/.env.template
@@ -1,7 +1,7 @@
 ## Bitwarden_RS Configuration File
 ## Uncomment any of the following lines to change the defaults
-## 
-## Be aware that most of these settings will be overridden if they were changed 
+##
+## Be aware that most of these settings will be overridden if they were changed
 ## in the admin interface. Those overrides are stored within DATA_FOLDER/config.json .
 
 ## Main data folder
@@ -70,7 +70,7 @@
 ## Log level
 ## Change the verbosity of the log output
 ## Valid values are "trace", "debug", "info", "warn", "error" and "off"
-## Setting it to "trace" or "debug" would also show logs for mounted 
+## Setting it to "trace" or "debug" would also show logs for mounted
 ## routes and static file, websocket and alive requests
 # LOG_LEVEL=Info
 
@@ -184,7 +184,7 @@
 ## Authenticator Settings
 ## Disable authenticator time drifted codes to be valid.
 ## TOTP codes of the previous and next 30 seconds will be invalid
-## 
+##
 ## According to the RFC6238 (https://tools.ietf.org/html/rfc6238),
 ## we allow by default the TOTP code which was valid one step back and one in the future.
 ## This can however allow attackers to be a bit more lucky with there attempts because there are 3 valid codes.
@@ -210,6 +210,9 @@
 # SMTP_EXPLICIT_TLS=true # N.B. This variable configures Implicit TLS. It's currently mislabelled (see bug #851)
 # SMTP_USERNAME=username
 # SMTP_PASSWORD=password
+## Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections.
+## Possible values: ["Plain", "Login", "Xoauth2"].
+## Multiple options need to be separated by a comma ','.
 # SMTP_AUTH_MECHANISM="Plain"
 # SMTP_TIMEOUT=15
 
diff --git a/src/config.rs b/src/config.rs
index 9491edf2..4e25674d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -400,7 +400,7 @@ make_config! {
         smtp_username:          String, true,   option;
         /// Password
         smtp_password:          Pass,   true,   option;
-        /// Json form auth mechanism |> Defaults for ssl is "Plain" and "Login" and nothing for non-ssl connections. Possible values: ["Plain", "Login", "Xoauth2"]
+        /// Json form auth mechanism |> Defaults for ssl is "Plain" and "Login" and nothing for non-ssl connections. Possible values: ["Plain", "Login", "Xoauth2"]. Multiple options need to be separated by a comma.
         smtp_auth_mechanism:    String, true,   option;
         /// SMTP connection timeout |> Number of seconds when to stop trying to connect to the SMTP server
         smtp_timeout:           u64,    true,   def,     15;
@@ -428,7 +428,7 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
 
     let dom = cfg.domain.to_lowercase();
     if !dom.starts_with("http://") && !dom.starts_with("https://") {
-        err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'"); 
+        err!("DOMAIN variable needs to contain the protocol (http, https). Use 'http[s]://bw.example.com' instead of 'bw.example.com'");
     }
 
     let whitelist = &cfg.signups_domains_whitelist;
diff --git a/src/mail.rs b/src/mail.rs
index a80b7ca5..2c6d7362 100644
--- a/src/mail.rs
+++ b/src/mail.rs
@@ -55,12 +55,22 @@ fn mailer() -> SmtpTransport {
 
     let smtp_client = match CONFIG.smtp_auth_mechanism() {
         Some(mechanism) => {
-            let correct_mechanism = format!("\"{}\"", crate::util::upcase_first(mechanism.trim_matches('"')));
-
-            // TODO: Allow more than one mechanism
-            match serde_json::from_str::<SmtpAuthMechanism>(&correct_mechanism) {
-                Ok(auth_mechanism) => smtp_client.authentication(vec![auth_mechanism]),
-                _ => panic!("Failure to parse mechanism. Is it proper Json? Eg. `\"Plain\"` not `Plain`"),
+            let allowed_mechanisms = vec![SmtpAuthMechanism::Plain, SmtpAuthMechanism::Login, SmtpAuthMechanism::Xoauth2];
+            let mut selected_mechanisms = vec![];
+            for wanted_mechanism in mechanism.split(',') {
+                for m in &allowed_mechanisms {
+                    if m.to_string().to_lowercase() == wanted_mechanism.trim_matches(|c| c == '"' || c == '\'' || c == ' ').to_lowercase() {
+                        selected_mechanisms.push(m.clone());
+                    }
+                }
+            };
+
+            if !selected_mechanisms.is_empty() {
+                smtp_client.authentication(selected_mechanisms)
+            } else {
+                // Only show a warning, and return without setting an actual authentication mechanism
+                warn!("No valid SMTP Auth mechanism found for '{}', using default values", mechanism);
+                smtp_client
             }
         }
         _ => smtp_client,
-- 
GitLab