commit 948554a20fd46acc7155303985b9517607b246b9
parent 9cdb605659f557ecfd5a90e00154fb0b58c8ec9b
Author: Daniel GarcĂa <dani-garcia@users.noreply.github.com>
Date: Thu, 13 Sep 2018 20:59:51 +0200
Added config option for websocket port, and reworked the config parsing a bit.
Added SMTP_FROM config to examples and made it mandatory, it doesn't make much sense to not specify the from address.
Diffstat:
6 files changed, 90 insertions(+), 49 deletions(-)
diff --git a/.env b/.env
@@ -14,6 +14,9 @@
# WEB_VAULT_FOLDER=web-vault/
# WEB_VAULT_ENABLED=true
+## Controls the WebSocket server port
+# WEBSOCKET_PORT=3012
+
## Controls if new users can register
# SIGNUPS_ALLOWED=true
@@ -42,8 +45,10 @@
# ROCKET_PORT=8000
# ROCKET_TLS={certs="/path/to/certs.pem",key="/path/to/key.pem"}
-## Mail specific settings, if SMTP_HOST is specified, SMTP_USERNAME and SMTP_PASSWORD are mandatory
+## Mail specific settings, set SMTP_HOST and SMTP_FROM to enable the mail service.
+## Note: if SMTP_USERNAME is specified, SMTP_PASSWORD is mandatory
# SMTP_HOST=smtp.domain.tld
+# SMTP_FROM=bitwarden-rs@domain.tld
# SMTP_PORT=587
# SMTP_SSL=true
# SMTP_USERNAME=username
diff --git a/README.md b/README.md
@@ -300,6 +300,7 @@ You can configure bitwarden_rs to send emails via a SMTP agent:
```sh
docker run -d --name bitwarden \
-e SMTP_HOST=<smtp.domain.tld> \
+ -e SMTP_FROM=<bitwarden@domain.tld> \
-e SMTP_PORT=587 \
-e SMTP_SSL=true \
-e SMTP_USERNAME=<username> \
diff --git a/src/api/identity.rs b/src/api/identity.rs
@@ -162,7 +162,7 @@ fn twofactor_auth(
return Ok(None);
}
- let provider = match util::parse_option_string(data.get_opt("twoFactorProvider")) {
+ let provider = match util::try_parse_string(data.get_opt("twoFactorProvider")) {
Some(provider) => provider,
None => providers[0], // If we aren't given a two factor provider, asume the first one
};
@@ -207,7 +207,7 @@ fn twofactor_auth(
_ => err!("Invalid two factor provider"),
}
- if util::parse_option_string(data.get_opt("twoFactorRemember")).unwrap_or(0) == 1 {
+ if util::try_parse_string_or(data.get_opt("twoFactorRemember"), 0) == 1 {
Ok(Some(device.refresh_twofactor_remember()))
} else {
device.delete_twofactor_remember();
@@ -274,7 +274,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for DeviceType {
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
let headers = request.headers();
let type_opt = headers.get_one("Device-Type");
- let type_num = util::parse_option_string(type_opt).unwrap_or(0);
+ let type_num = util::try_parse_string_or(type_opt, 0);
Outcome::Success(DeviceType(type_num))
}
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
@@ -5,6 +5,8 @@ use api::JsonResult;
use auth::Headers;
use db::DbConn;
+use CONFIG;
+
pub fn routes() -> Vec<Route> {
routes![negotiate, websockets_err]
}
@@ -356,7 +358,7 @@ pub fn start_notification_server() -> WebSocketUsers {
thread::spawn(move || {
WebSocket::new(factory)
.unwrap()
- .listen("0.0.0.0:3012")
+ .listen(format!("0.0.0.0:{}", CONFIG.websocket_port))
.unwrap();
});
diff --git a/src/main.rs b/src/main.rs
@@ -1,4 +1,4 @@
-#![feature(plugin, custom_derive, vec_remove_item)]
+#![feature(plugin, custom_derive, vec_remove_item, try_trait)]
#![plugin(rocket_codegen)]
#![allow(proc_macro_derive_resolution_fallback)] // TODO: Remove this when diesel update fixes warnings
extern crate rocket;
@@ -36,7 +36,7 @@ extern crate native_tls;
extern crate fast_chemail;
extern crate byteorder;
-use std::{env, path::Path, process::{exit, Command}};
+use std::{path::Path, process::{exit, Command}};
use rocket::Rocket;
#[macro_use]
@@ -78,7 +78,7 @@ mod migrations {
fn main() {
check_db();
check_rsa_keys();
- check_web_vault();
+ check_web_vault();
migrations::run_migrations();
init_rocket().launch();
@@ -176,27 +176,32 @@ pub struct MailConfig {
impl MailConfig {
fn load() -> Option<Self> {
- let smtp_host = env::var("SMTP_HOST").ok();
-
+ use util::{get_env, get_env_or};
+
// When SMTP_HOST is absent, we assume the user does not want to enable it.
- if smtp_host.is_none() {
- return None
- }
+ let smtp_host = match get_env("SMTP_HOST") {
+ Some(host) => host,
+ None => return None,
+ };
+
+ let smtp_from = get_env("SMTP_FROM").unwrap_or_else(|| {
+ println!("Please specify SMTP_FROM to enable SMTP support.");
+ exit(1);
+ });
- let smtp_ssl = util::parse_option_string(env::var("SMTP_SSL").ok()).unwrap_or(true);
- let smtp_port = util::parse_option_string(env::var("SMTP_PORT").ok())
- .unwrap_or_else(|| {
- if smtp_ssl {
- 587u16
- } else {
- 25u16
- }
- });
-
- let smtp_username = env::var("SMTP_USERNAME").ok();
- let smtp_password = env::var("SMTP_PASSWORD").ok().or_else(|| {
+ 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() {
- println!("Please specify SMTP_PASSWORD to enable SMTP support.");
+ println!("SMTP_PASSWORD is mandatory when specifying SMTP_USERNAME.");
exit(1);
} else {
None
@@ -204,13 +209,12 @@ impl MailConfig {
});
Some(MailConfig {
- smtp_host: smtp_host.unwrap(),
- smtp_port: smtp_port,
- smtp_ssl: smtp_ssl,
- smtp_from: util::parse_option_string(env::var("SMTP_FROM").ok())
- .unwrap_or("bitwarden-rs@localhost".to_string()),
- smtp_username: smtp_username,
- smtp_password: smtp_password,
+ smtp_host,
+ smtp_port,
+ smtp_ssl,
+ smtp_from,
+ smtp_username,
+ smtp_password,
})
}
}
@@ -228,6 +232,8 @@ pub struct Config {
web_vault_folder: String,
web_vault_enabled: bool,
+ websocket_port: i32,
+
local_icon_extractor: bool,
signups_allowed: bool,
invitations_allowed: bool,
@@ -242,32 +248,35 @@ pub struct Config {
impl Config {
fn load() -> Self {
+ use util::{get_env, get_env_or};
dotenv::dotenv().ok();
- let df = env::var("DATA_FOLDER").unwrap_or("data".into());
- let key = env::var("RSA_KEY_FILENAME").unwrap_or(format!("{}/{}", &df, "rsa_key"));
+ let df = get_env_or("DATA_FOLDER", "data".to_string());
+ let key = get_env_or("RSA_KEY_FILENAME", format!("{}/{}", &df, "rsa_key"));
- let domain = env::var("DOMAIN");
+ let domain = get_env("DOMAIN");
Config {
- database_url: env::var("DATABASE_URL").unwrap_or(format!("{}/{}", &df, "db.sqlite3")),
- icon_cache_folder: env::var("ICON_CACHE_FOLDER").unwrap_or(format!("{}/{}", &df, "icon_cache")),
- attachments_folder: env::var("ATTACHMENTS_FOLDER").unwrap_or(format!("{}/{}", &df, "attachments")),
+ 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")),
private_rsa_key: format!("{}.der", &key),
private_rsa_key_pem: format!("{}.pem", &key),
public_rsa_key: format!("{}.pub.der", &key),
- web_vault_folder: env::var("WEB_VAULT_FOLDER").unwrap_or("web-vault/".into()),
- web_vault_enabled: util::parse_option_string(env::var("WEB_VAULT_ENABLED").ok()).unwrap_or(true),
+ web_vault_folder: get_env_or("WEB_VAULT_FOLDER", "web-vault/".into()),
+ web_vault_enabled: get_env_or("WEB_VAULT_ENABLED", true),
+
+ websocket_port: get_env_or("WEBSOCKET_PORT", 3012),
- local_icon_extractor: util::parse_option_string(env::var("LOCAL_ICON_EXTRACTOR").ok()).unwrap_or(false),
- signups_allowed: util::parse_option_string(env::var("SIGNUPS_ALLOWED").ok()).unwrap_or(true),
- invitations_allowed: util::parse_option_string(env::var("INVITATIONS_ALLOWED").ok()).unwrap_or(true),
- password_iterations: util::parse_option_string(env::var("PASSWORD_ITERATIONS").ok()).unwrap_or(100_000),
- show_password_hint: util::parse_option_string(env::var("SHOW_PASSWORD_HINT").ok()).unwrap_or(true),
+ local_icon_extractor: get_env_or("LOCAL_ICON_EXTRACTOR", false),
+ signups_allowed: get_env_or("SIGNUPS_ALLOWED", true),
+ 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_ok(),
+ domain_set: domain.is_some(),
domain: domain.unwrap_or("http://localhost".into()),
mail: MailConfig::load(),
diff --git a/src/util.rs b/src/util.rs
@@ -97,6 +97,7 @@ pub fn get_display_size(size: i32) -> String {
///
use std::str::FromStr;
+use std::ops::Try;
pub fn upcase_first(s: &str) -> String {
let mut c = s.chars();
@@ -106,14 +107,37 @@ pub fn upcase_first(s: &str) -> String {
}
}
-pub fn parse_option_string<S, T>(string: Option<S>) -> Option<T> where S: AsRef<str>, T: FromStr {
- if let Some(Ok(value)) = string.map(|s| s.as_ref().parse::<T>()) {
+pub fn try_parse_string<S, T, U>(string: impl Try<Ok = S, Error=U>) -> Option<T> where S: AsRef<str>, T: FromStr {
+ if let Ok(Ok(value)) = string.into_result().map(|s| s.as_ref().parse::<T>()) {
Some(value)
} else {
None
}
}
+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
+///
+
+use std::env;
+
+pub fn get_env<V>(key: &str) -> Option<V> where V: FromStr {
+ 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
///