commit 86ed75bf7c95d35b41b4e5553b5aece708b4a3cd
parent 20d8d800f380ad5ade821f7485916f321fdda1cf
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Sat, 2 Feb 2019 01:09:21 +0100
Config can now be serialized / deserialized
Diffstat:
8 files changed, 214 insertions(+), 268 deletions(-)
diff --git a/src/api/admin.rs b/src/api/admin.rs
@@ -24,6 +24,8 @@ pub fn routes() -> Vec<Route> {
invite_user,
delete_user,
deauth_user,
+ get_config,
+ post_config,
]
}
@@ -136,11 +138,11 @@ fn invite_user(data: JsonUpcase<InviteData>, _token: AdminToken, conn: DbConn) -
err!("Invitations are not allowed")
}
- if let Some(ref mail_config) = CONFIG.mail() {
+ if CONFIG.mail_enabled() {
let mut user = User::new(email);
user.save(&conn)?;
let org_name = "bitwarden_rs";
- mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None, mail_config)
+ mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None)
} else {
let mut invitation = Invitation::new(data.Email);
invitation.save(&conn)
@@ -169,6 +171,20 @@ fn deauth_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
user.save(&conn)
}
+#[get("/config")]
+fn get_config(_token: AdminToken) -> EmptyResult {
+ unimplemented!("Get config")
+}
+
+#[post("/config", data = "<data>")]
+fn post_config(data: JsonUpcase<Value>, _token: AdminToken) -> EmptyResult {
+ let data: Value = data.into_inner().data;
+
+ info!("CONFIG: {:#?}", data);
+
+ unimplemented!("Update config")
+}
+
pub struct AdminToken {}
impl<'a, 'r> FromRequest<'a, 'r> for AdminToken {
diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs
@@ -419,8 +419,8 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
None => return Ok(()),
};
- if let Some(ref mail_config) = CONFIG.mail() {
- mail::send_password_hint(&data.Email, hint, mail_config)?;
+ if CONFIG.mail_enabled() {
+ mail::send_password_hint(&data.Email, hint)?;
} else if CONFIG.show_password_hint() {
if let Some(hint) = hint {
err!(format!("Your password hint is: {}", &hint));
diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs
@@ -486,9 +486,9 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
}
for email in data.Emails.iter() {
- let mut user_org_status = match CONFIG.mail() {
- Some(_) => UserOrgStatus::Invited as i32,
- None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
+ let mut user_org_status = match CONFIG.mail_enabled() {
+ true => UserOrgStatus::Invited as i32,
+ false => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
};
let user = match User::find_by_mail(&email, &conn) {
None => {
@@ -496,7 +496,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
err!(format!("User email does not exist: {}", email))
}
- if CONFIG.mail().is_none() {
+ if !CONFIG.mail_enabled() {
let mut invitation = Invitation::new(email.clone());
invitation.save(&conn)?;
}
@@ -535,7 +535,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
new_user.save(&conn)?;
- if let Some(ref mail_config) = CONFIG.mail() {
+ if CONFIG.mail_enabled() {
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
Some(org) => org.name,
None => err!("Error looking up organization"),
@@ -548,7 +548,6 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
Some(new_user.uuid),
&org_name,
Some(headers.user.email.clone()),
- mail_config,
)?;
}
}
@@ -562,7 +561,7 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn:
err!("Invitations are not allowed.")
}
- if CONFIG.mail().is_none() {
+ if !CONFIG.mail_enabled() {
err!("SMTP is not configured.")
}
@@ -585,7 +584,7 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn:
None => err!("Error looking up organization."),
};
- if let Some(ref mail_config) = CONFIG.mail() {
+ if CONFIG.mail_enabled() {
mail::send_invite(
&user.email,
&user.uuid,
@@ -593,7 +592,6 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn:
Some(user_org.uuid),
&org_name,
Some(headers.user.email),
- mail_config,
)?;
} else {
let mut invitation = Invitation::new(user.email.clone());
@@ -637,7 +635,7 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
None => err!("Invited user not found"),
}
- if let Some(ref mail_config) = CONFIG.mail() {
+ if CONFIG.mail_enabled() {
let mut org_name = String::from("bitwarden_rs");
if let Some(org_id) = &claims.org_id {
org_name = match Organization::find_by_uuid(&org_id, &conn) {
@@ -647,10 +645,10 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
};
if let Some(invited_by_email) = &claims.invited_by_email {
// User was invited to an organization, so they must be confirmed manually after acceptance
- mail::send_invite_accepted(&claims.email, invited_by_email, &org_name, mail_config)?;
+ mail::send_invite_accepted(&claims.email, invited_by_email, &org_name)?;
} else {
// User was invited from /admin, so they are automatically confirmed
- mail::send_invite_confirmed(&claims.email, &org_name, mail_config)?;
+ mail::send_invite_confirmed(&claims.email, &org_name)?;
}
}
@@ -686,7 +684,7 @@ fn confirm_invite(
None => err!("Invalid key provided"),
};
- if let Some(ref mail_config) = CONFIG.mail() {
+ if CONFIG.mail_enabled() {
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
Some(org) => org.name,
None => err!("Error looking up organization."),
@@ -695,7 +693,7 @@ fn confirm_invite(
Some(user) => user.email,
None => err!("Error looking up user."),
};
- mail::send_invite_confirmed(&address, &org_name, mail_config)?;
+ mail::send_invite_confirmed(&address, &org_name)?;
}
user_to_confirm.save(&conn)
diff --git a/src/api/core/two_factor.rs b/src/api/core/two_factor.rs
@@ -3,15 +3,14 @@ use rocket_contrib::json::Json;
use serde_json;
use serde_json::Value;
+use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
+use crate::auth::Headers;
+use crate::crypto;
use crate::db::{
models::{TwoFactor, TwoFactorType, User},
DbConn,
};
-
-use crate::crypto;
-
-use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, NumberOrString, PasswordData};
-use crate::auth::Headers;
+use crate::error::{Error, MapResult};
use rocket::Route;
@@ -508,32 +507,31 @@ fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
result
}
-fn verify_yubikey_otp(otp: String) -> JsonResult {
- if !CONFIG.yubico_cred_set() {
- err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
+fn get_yubico_credentials() -> Result<(String, String), Error> {
+ match (CONFIG.yubico_client_id(), CONFIG.yubico_secret_key()) {
+ (Some(id), Some(secret)) => Ok((id, secret)),
+ _ => err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled"),
}
+}
+
+fn verify_yubikey_otp(otp: String) -> EmptyResult {
+ let (yubico_id, yubico_secret) = get_yubico_credentials()?;
let yubico = Yubico::new();
- let config = Config::default()
- .set_client_id(CONFIG.yubico_client_id())
- .set_key(CONFIG.yubico_secret_key());
+ let config = Config::default().set_client_id(yubico_id).set_key(yubico_secret);
- let result = match CONFIG.yubico_server() {
+ match CONFIG.yubico_server() {
Some(server) => yubico.verify(otp, config.set_api_hosts(vec![server])),
None => yubico.verify(otp, config),
- };
-
- match result {
- Ok(_answer) => Ok(Json(json!({}))),
- Err(_e) => err!("Failed to verify OTP"),
}
+ .map_res("Failed to verify OTP")
+ .and(Ok(()))
}
#[post("/two-factor/get-yubikey", data = "<data>")]
fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
- if !CONFIG.yubico_cred_set() {
- err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
- }
+ // Make sure the credentials are set
+ get_yubico_credentials()?;
let data: PasswordData = data.into_inner().data;
let user = headers.user;
@@ -597,11 +595,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
continue;
}
- let result = verify_yubikey_otp(yubikey.to_owned());
-
- if let Err(_e) = result {
- err!("Invalid Yubikey OTP provided");
- }
+ verify_yubikey_otp(yubikey.to_owned()).map_res("Invalid Yubikey OTP provided")?;
}
let yubikey_ids: Vec<String> = yubikeys.into_iter().map(|x| (&x[..12]).to_owned()).collect();
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
@@ -355,7 +355,7 @@ pub fn start_notification_server() -> WebSocketUsers {
thread::spawn(move || {
WebSocket::new(factory)
.unwrap()
- .listen(&CONFIG.websocket_url())
+ .listen((CONFIG.websocket_address().as_str(), CONFIG.websocket_port()))
.unwrap();
});
}
diff --git a/src/config.rs b/src/config.rs
@@ -3,77 +3,149 @@ use std::sync::RwLock;
use handlebars::Handlebars;
+use crate::error::Error;
+use crate::util::IntoResult;
+
lazy_static! {
- pub static ref CONFIG: Config = Config::load();
+ pub static ref CONFIG: Config = Config::load().unwrap_or_else(|e| {
+ println!("Error loading config:\n\t{:?}\n", e);
+ exit(12)
+ });
}
macro_rules! make_config {
- ( $( $name:ident: $ty:ty ),+ $(,)* ) => {
+ ( $( $name:ident : $ty:ty $(, $default_fn:expr)? );+ $(;)* ) => {
- pub struct Config { inner: RwLock<_Config> }
+ pub struct Config { inner: RwLock<Inner> }
- #[derive(Default)]
- struct _Config {
- _templates: Handlebars,
- $(pub $name: $ty),+
+ struct Inner {
+ templates: Handlebars,
+ config: _Config,
}
+ #[derive(Debug, Default, Serialize, Deserialize)]
+ struct _Config { $(pub $name: $ty),+ }
+
paste::item! {
- #[allow(unused)]
- impl Config {
+ #[allow(unused)]
+ impl Config {
+ $(
+ pub fn $name(&self) -> $ty {
+ self.inner.read().unwrap().config.$name.clone()
+ }
+ pub fn [<set_ $name>](&self, value: $ty) {
+ self.inner.write().unwrap().config.$name = value;
+ }
+ )+
+
+ pub fn load() -> Result<Self, Error> {
+ use crate::util::get_env;
+ dotenv::dotenv().ok();
+
+ let mut config = _Config::default();
+
$(
- pub fn $name(&self) -> $ty {
- self.inner.read().unwrap().$name.clone()
- }
- pub fn [<set_ $name>](&self, value: $ty) {
- self.inner.write().unwrap().$name = value;
- }
+ config.$name = make_config!{ @expr &stringify!($name).to_uppercase(), $ty, &config, $($default_fn)? };
)+
+
+ Ok(Config {
+ inner: RwLock::new(Inner {
+ templates: load_templates(&config.templates_folder),
+ config,
+ }),
+ })
}
}
+ }
};
-}
-
-make_config! {
- database_url: String,
- icon_cache_folder: String,
- attachments_folder: String,
-
- icon_cache_ttl: u64,
- icon_cache_negttl: u64,
-
- private_rsa_key: String,
- private_rsa_key_pem: String,
- public_rsa_key: String,
- web_vault_folder: String,
- web_vault_enabled: bool,
-
- websocket_enabled: bool,
- websocket_url: String,
-
- extended_logging: bool,
- log_file: Option<String>,
+ ( @expr $name:expr, $ty:ty, $config:expr, $default_fn:expr ) => {{
+ match get_env($name) {
+ Some(v) => v,
+ None => {
+ let f: &Fn(&_Config) -> _ = &$default_fn;
+ f($config).into_result()?
+ }
+ }
+ }};
- disable_icon_download: bool,
- signups_allowed: bool,
- invitations_allowed: bool,
- admin_token: Option<String>,
- password_iterations: i32,
- show_password_hint: bool,
+ ( @expr $name:expr, $ty:ty, $config:expr, ) => {
+ get_env($name)
+ };
+}
- domain: String,
- domain_set: bool,
+make_config! {
+ data_folder: String, |_| "data".to_string();
+ database_url: String, |c| format!("{}/{}", c.data_folder, "db.sqlite3");
+ icon_cache_folder: String, |c| format!("{}/{}", c.data_folder, "icon_cache");
+ attachments_folder: String, |c| format!("{}/{}", c.data_folder, "attachments");
+ templates_folder: String, |c| format!("{}/{}", c.data_folder, "templates");
+
+ rsa_key_filename: String, |c| format!("{}/{}", c.data_folder, "rsa_key");
+ private_rsa_key: String, |c| format!("{}.der", c.rsa_key_filename);
+ private_rsa_key_pem: String, |c| format!("{}.pem", c.rsa_key_filename);
+ public_rsa_key: String, |c| format!("{}.pub.der", c.rsa_key_filename);
+
+ websocket_enabled: bool, |_| false;
+ websocket_address: String, |_| "0.0.0.0".to_string();
+ websocket_port: u16, |_| 3012;
+
+ web_vault_folder: String, |_| "web-vault/".to_string();
+ web_vault_enabled: bool, |_| true;
+
+ icon_cache_ttl: u64, |_| 2_592_000;
+ icon_cache_negttl: u64, |_| 259_200;
+
+ disable_icon_download: bool, |_| false;
+ signups_allowed: bool, |_| true;
+ invitations_allowed: bool, |_| true;
+ password_iterations: i32, |_| 100_000;
+ show_password_hint: bool, |_| true;
+
+ domain: String, |_| "http://localhost".to_string();
+ domain_set: bool, |_| false;
+
+ reload_templates: bool, |_| false;
+
+ extended_logging: bool, |_| true;
+ log_file: Option<String>;
+
+ admin_token: Option<String>;
+
+ yubico_client_id: Option<String>;
+ yubico_secret_key: Option<String>;
+ yubico_server: Option<String>;
+
+ // Mail settings
+ smtp_host: Option<String>;
+ smtp_ssl: bool, |_| true;
+ smtp_port: u16, |c| if c.smtp_ssl {587} else {25};
+ smtp_from: String, |c| if c.smtp_host.is_some() { err!("Please specify SMTP_FROM to enable SMTP support") } else { Ok(String::new() )};
+ smtp_from_name: String, |_| "Bitwarden_RS".to_string();
+ smtp_username: Option<String>;
+ smtp_password: Option<String>;
+}
- yubico_cred_set: bool,
- yubico_client_id: String,
- yubico_secret_key: String,
- yubico_server: Option<String>,
+impl Config {
+ pub fn mail_enabled(&self) -> bool {
+ self.inner.read().unwrap().config.smtp_host.is_some()
+ }
- mail: Option<MailConfig>,
- templates_folder: String,
- reload_templates: bool,
+ pub fn render_template<T: serde::ser::Serialize>(
+ &self,
+ name: &str,
+ data: &T,
+ ) -> Result<String, crate::error::Error> {
+ if CONFIG.reload_templates() {
+ warn!("RELOADING TEMPLATES");
+ let hb = load_templates(CONFIG.templates_folder().as_ref());
+ hb.render(name, data).map_err(Into::into)
+ } else {
+ let hb = &CONFIG.inner.read().unwrap().templates;
+ hb.render(name, data).map_err(Into::into)
+ }
+ }
}
fn load_templates(path: &str) -> Handlebars {
@@ -106,140 +178,3 @@ fn load_templates(path: &str) -> Handlebars {
hb
}
-
-impl Config {
- pub fn render_template<T: serde::ser::Serialize>(
- &self,
- name: &str,
- data: &T,
- ) -> Result<String, crate::error::Error> {
- if CONFIG.reload_templates() {
- warn!("RELOADING TEMPLATES");
- let hb = load_templates(CONFIG.templates_folder().as_ref());
- hb.render(name, data).map_err(Into::into)
- } else {
- let hb = &CONFIG.inner.read().unwrap()._templates;
- hb.render(name, data).map_err(Into::into)
- }
- }
-
- fn load() -> Self {
- use crate::util::{get_env, get_env_or};
- dotenv::dotenv().ok();
-
- let df = get_env_or("DATA_FOLDER", "data".to_string());
- let key = get_env_or("RSA_KEY_FILENAME", format!("{}/{}", &df, "rsa_key"));
-
- let domain = get_env("DOMAIN");
-
- let yubico_client_id = get_env("YUBICO_CLIENT_ID");
- let yubico_secret_key = get_env("YUBICO_SECRET_KEY");
-
- let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"));
-
- let cfg = _Config {
- database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")),
- icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")),
- attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")),
- _templates: load_templates(&templates_folder),
- templates_folder,
- reload_templates: get_env_or("RELOAD_TEMPLATES", false),
-
- // icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds)
- icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000),
- // icon_cache_negttl defaults to 3 days (3 * 24 * 60 * 60 seconds)
- icon_cache_negttl: get_env_or("ICON_CACHE_NEGTTL", 259_200),
-
- private_rsa_key: format!("{}.der", &key),
- private_rsa_key_pem: format!("{}.pem", &key),
- public_rsa_key: format!("{}.pub.der", &key),
-
- web_vault_folder: get_env_or("WEB_VAULT_FOLDER", "web-vault/".into()),
- web_vault_enabled: get_env_or("WEB_VAULT_ENABLED", true),
-
- websocket_enabled: get_env_or("WEBSOCKET_ENABLED", false),
- websocket_url: format!(
- "{}:{}",
- get_env_or("WEBSOCKET_ADDRESS", "0.0.0.0".to_string()),
- get_env_or("WEBSOCKET_PORT", 3012)
- ),
-
- extended_logging: get_env_or("EXTENDED_LOGGING", true),
- log_file: get_env("LOG_FILE"),
-
- disable_icon_download: get_env_or("DISABLE_ICON_DOWNLOAD", false),
- signups_allowed: get_env_or("SIGNUPS_ALLOWED", true),
- admin_token: get_env("ADMIN_TOKEN"),
- invitations_allowed: get_env_or("INVITATIONS_ALLOWED", true),
- password_iterations: get_env_or("PASSWORD_ITERATIONS", 100_000),
- show_password_hint: get_env_or("SHOW_PASSWORD_HINT", true),
-
- domain_set: domain.is_some(),
- domain: domain.unwrap_or("http://localhost".into()),
-
- yubico_cred_set: yubico_client_id.is_some() && yubico_secret_key.is_some(),
- yubico_client_id: yubico_client_id.unwrap_or("00000".into()),
- yubico_secret_key: yubico_secret_key.unwrap_or("AAAAAAA".into()),
- yubico_server: get_env("YUBICO_SERVER"),
-
- mail: MailConfig::load(),
- };
-
- Config {
- inner: RwLock::new(cfg),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct MailConfig {
- pub smtp_host: String,
- pub smtp_port: u16,
- pub smtp_ssl: bool,
- pub smtp_from: String,
- pub smtp_from_name: String,
- pub smtp_username: Option<String>,
- pub smtp_password: Option<String>,
-}
-
-impl MailConfig {
- fn load() -> Option<Self> {
- use crate::util::{get_env, get_env_or};
-
- // When SMTP_HOST is absent, we assume the user does not want to enable it.
- let smtp_host = match get_env("SMTP_HOST") {
- Some(host) => host,
- None => return None,
- };
-
- let smtp_from = get_env("SMTP_FROM").unwrap_or_else(|| {
- error!("Please specify SMTP_FROM to enable SMTP support.");
- exit(1);
- });
-
- let smtp_from_name = get_env_or("SMTP_FROM_NAME", "Bitwarden_RS".into());
-
- let smtp_ssl = get_env_or("SMTP_SSL", true);
- let smtp_port = get_env("SMTP_PORT").unwrap_or_else(|| if smtp_ssl { 587u16 } else { 25u16 });
-
- let smtp_username = get_env("SMTP_USERNAME");
- let smtp_password = get_env("SMTP_PASSWORD").or_else(|| {
- if smtp_username.as_ref().is_some() {
- error!("SMTP_PASSWORD is mandatory when specifying SMTP_USERNAME.");
- exit(1);
- } else {
- None
- }
- });
-
- Some(MailConfig {
- smtp_host,
- smtp_port,
- smtp_ssl,
- smtp_from,
- smtp_from_name,
- smtp_username,
- smtp_password,
- })
- }
-}
diff --git a/src/mail.rs b/src/mail.rs
@@ -6,25 +6,26 @@ use native_tls::{Protocol, TlsConnector};
use crate::api::EmptyResult;
use crate::auth::{encode_jwt, generate_invite_claims};
-use crate::config::MailConfig;
use crate::error::Error;
use crate::CONFIG;
-fn mailer(config: &MailConfig) -> SmtpTransport {
- let client_security = if config.smtp_ssl {
+fn mailer() -> SmtpTransport {
+ let host = CONFIG.smtp_host().unwrap();
+
+ let client_security = if CONFIG.smtp_ssl() {
let tls = TlsConnector::builder()
.min_protocol_version(Some(Protocol::Tlsv11))
.build()
.unwrap();
- ClientSecurity::Required(ClientTlsParameters::new(config.smtp_host.clone(), tls))
+ ClientSecurity::Required(ClientTlsParameters::new(host.clone(), tls))
} else {
ClientSecurity::None
};
- let smtp_client = SmtpClient::new((config.smtp_host.as_str(), config.smtp_port), client_security).unwrap();
+ let smtp_client = SmtpClient::new((host.as_str(), CONFIG.smtp_port()), client_security).unwrap();
- let smtp_client = match (&config.smtp_username, &config.smtp_password) {
+ let smtp_client = match (&CONFIG.smtp_username(), &CONFIG.smtp_password()) {
(Some(user), Some(pass)) => smtp_client.credentials(Credentials::new(user.clone(), pass.clone())),
_ => smtp_client,
};
@@ -52,7 +53,7 @@ fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(Str
Ok((subject, body))
}
-pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConfig) -> EmptyResult {
+pub fn send_password_hint(address: &str, hint: Option<String>) -> EmptyResult {
let template_name = if hint.is_some() {
"email/pw_hint_some"
} else {
@@ -61,7 +62,7 @@ pub fn send_password_hint(address: &str, hint: Option<String>, config: &MailConf
let (subject, body) = get_text(template_name, json!({ "hint": hint }))?;
- send_email(&address, &subject, &body, &config)
+ send_email(&address, &subject, &body)
}
pub fn send_invite(
@@ -71,7 +72,6 @@ pub fn send_invite(
org_user_id: Option<String>,
org_name: &str,
invited_by_email: Option<String>,
- config: &MailConfig,
) -> EmptyResult {
let claims = generate_invite_claims(
uuid.to_string(),
@@ -94,10 +94,10 @@ pub fn send_invite(
}),
)?;
- send_email(&address, &subject, &body, &config)
+ send_email(&address, &subject, &body)
}
-pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str, config: &MailConfig) -> EmptyResult {
+pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str) -> EmptyResult {
let (subject, body) = get_text(
"email/invite_accepted",
json!({
@@ -107,10 +107,10 @@ pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str,
}),
)?;
- send_email(&address, &subject, &body, &config)
+ send_email(&address, &subject, &body)
}
-pub fn send_invite_confirmed(address: &str, org_name: &str, config: &MailConfig) -> EmptyResult {
+pub fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult {
let (subject, body) = get_text(
"email/invite_confirmed",
json!({
@@ -119,20 +119,20 @@ pub fn send_invite_confirmed(address: &str, org_name: &str, config: &MailConfig)
}),
)?;
- send_email(&address, &subject, &body, &config)
+ send_email(&address, &subject, &body)
}
-fn send_email(address: &str, subject: &str, body: &str, config: &MailConfig) -> EmptyResult {
+fn send_email(address: &str, subject: &str, body: &str) -> EmptyResult {
let email = EmailBuilder::new()
.to(address)
- .from((config.smtp_from.as_str(), config.smtp_from_name.as_str()))
+ .from((CONFIG.smtp_from().as_str(), CONFIG.smtp_from_name().as_str()))
.subject(subject)
.header(("Content-Type", "text/html"))
.body(body)
.build()
.map_err(|e| Error::new("Error building email", e.to_string()))?;
- mailer(config)
+ mailer()
.send(email.into())
.map_err(|e| Error::new("Error sending email", e.to_string()))
.and(Ok(()))
diff --git a/src/util.rs b/src/util.rs
@@ -140,18 +140,6 @@ where
}
}
-pub fn try_parse_string_or<S, T, U>(string: impl Try<Ok = S, Error = U>, default: T) -> T
-where
- S: AsRef<str>,
- T: FromStr,
-{
- if let Ok(Ok(value)) = string.into_result().map(|s| s.as_ref().parse::<T>()) {
- value
- } else {
- default
- }
-}
-
//
// Env methods
//
@@ -165,13 +153,6 @@ where
try_parse_string(env::var(key))
}
-pub fn get_env_or<V>(key: &str, default: V) -> V
-where
- V: FromStr,
-{
- try_parse_string_or(env::var(key), default)
-}
-
//
// Date util methods
//
@@ -303,3 +284,25 @@ where
}
}
}
+
+
+//
+// Into Result
+//
+use crate::error::Error;
+
+pub trait IntoResult<T> {
+ fn into_result(self) -> Result<T, Error>;
+}
+
+impl<T> IntoResult<T> for Result<T, Error> {
+ fn into_result(self) -> Result<T, Error> {
+ self
+ }
+}
+
+impl<T> IntoResult<T> for T {
+ fn into_result(self) -> Result<T, Error> {
+ Ok(self)
+ }
+}