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.
This commit is contained in:
Daniel García 2018-09-13 20:59:51 +02:00
parent 9cdb605659
commit 948554a20f
No known key found for this signature in database
GPG key ID: FC8A7D14C3CD543A
6 changed files with 89 additions and 48 deletions

7
.env
View file

@ -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

View file

@ -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> \

View file

@ -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))
}

View file

@ -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();
});

View file

@ -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]
@ -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_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_from = get_env("SMTP_FROM").unwrap_or_else(|| {
println!("Please specify SMTP_FROM to enable SMTP support.");
exit(1);
});
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),
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),
websocket_port: get_env_or("WEBSOCKET_PORT", 3012),
domain_set: domain.is_ok(),
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_some(),
domain: domain.unwrap_or("http://localhost".into()),
mail: MailConfig::load(),

View file

@ -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
///