2022-03-26 17:00:37 +00:00
|
|
|
use crate::{
|
|
|
|
domain::handler::UserId,
|
2022-05-05 13:53:58 +00:00
|
|
|
infra::cli::{GeneralConfigOpts, LdapsOpts, RunOpts, SmtpOpts, TestEmailOpts},
|
2022-03-26 17:00:37 +00:00
|
|
|
};
|
2021-08-26 19:56:42 +00:00
|
|
|
use anyhow::{Context, Result};
|
2021-03-02 19:30:43 +00:00
|
|
|
use figment::{
|
|
|
|
providers::{Env, Format, Serialized, Toml},
|
|
|
|
Figment,
|
|
|
|
};
|
2021-11-03 08:09:19 +00:00
|
|
|
use lettre::message::Mailbox;
|
2021-08-31 14:29:49 +00:00
|
|
|
use lldap_auth::opaque::{server::ServerSetup, KeyPair};
|
2021-11-11 09:36:42 +00:00
|
|
|
use secstr::SecUtf8;
|
2021-03-02 19:30:43 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-03-02 19:13:58 +00:00
|
|
|
|
2021-11-03 08:09:19 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
|
|
|
#[builder(pattern = "owned")]
|
|
|
|
pub struct MailOptions {
|
|
|
|
#[builder(default = "false")]
|
|
|
|
pub enable_password_reset: bool,
|
|
|
|
#[builder(default = "None")]
|
|
|
|
pub from: Option<Mailbox>,
|
|
|
|
#[builder(default = "None")]
|
|
|
|
pub reply_to: Option<Mailbox>,
|
|
|
|
#[builder(default = r#""localhost".to_string()"#)]
|
|
|
|
pub server: String,
|
|
|
|
#[builder(default = "587")]
|
|
|
|
pub port: u16,
|
|
|
|
#[builder(default = r#""admin".to_string()"#)]
|
|
|
|
pub user: String,
|
2021-11-11 09:36:42 +00:00
|
|
|
#[builder(default = r#"SecUtf8::from("")"#)]
|
|
|
|
pub password: SecUtf8,
|
2021-11-03 08:09:19 +00:00
|
|
|
#[builder(default = "true")]
|
|
|
|
pub tls_required: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::default::Default for MailOptions {
|
|
|
|
fn default() -> Self {
|
|
|
|
MailOptionsBuilder::default().build().unwrap()
|
|
|
|
}
|
|
|
|
}
|
2021-03-02 19:51:33 +00:00
|
|
|
|
2022-05-05 13:53:58 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
|
|
|
#[builder(pattern = "owned")]
|
|
|
|
pub struct LdapsOptions {
|
|
|
|
#[builder(default = "false")]
|
|
|
|
pub enabled: bool,
|
|
|
|
#[builder(default = "6360")]
|
|
|
|
pub port: u16,
|
|
|
|
#[builder(default = r#"String::from("cert.pem")"#)]
|
|
|
|
pub cert_file: String,
|
|
|
|
#[builder(default = r#"String::from("key.pem")"#)]
|
|
|
|
pub key_file: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::default::Default for LdapsOptions {
|
|
|
|
fn default() -> Self {
|
|
|
|
LdapsOptionsBuilder::default().build().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
2021-11-27 15:34:50 +00:00
|
|
|
#[builder(pattern = "owned", build_fn(name = "private_build"))]
|
2021-03-02 19:30:43 +00:00
|
|
|
pub struct Configuration {
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "3890")]
|
2021-03-02 22:07:01 +00:00
|
|
|
pub ldap_port: u16,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "17170")]
|
2021-03-07 11:36:12 +00:00
|
|
|
pub http_port: u16,
|
2021-11-11 09:36:42 +00:00
|
|
|
#[builder(default = r#"SecUtf8::from("secretjwtsecret")"#)]
|
|
|
|
pub jwt_secret: SecUtf8,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("dc=example,dc=com")"#)]
|
2021-03-16 17:27:31 +00:00
|
|
|
pub ldap_base_dn: String,
|
2022-03-26 17:00:37 +00:00
|
|
|
#[builder(default = r#"UserId::new("admin")"#)]
|
|
|
|
pub ldap_user_dn: UserId,
|
2021-11-11 09:36:42 +00:00
|
|
|
#[builder(default = r#"SecUtf8::from("password")"#)]
|
|
|
|
pub ldap_user_pass: SecUtf8,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("sqlite://users.db?mode=rwc")"#)]
|
2021-03-12 08:33:43 +00:00
|
|
|
pub database_url: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "false")]
|
2021-03-02 22:07:01 +00:00
|
|
|
pub verbose: bool,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("server_key")"#)]
|
2021-06-14 14:02:36 +00:00
|
|
|
pub key_file: String,
|
2021-11-03 08:09:19 +00:00
|
|
|
#[builder(default)]
|
|
|
|
pub smtp_options: MailOptions,
|
2022-05-05 13:53:58 +00:00
|
|
|
#[builder(default)]
|
|
|
|
pub ldaps_options: LdapsOptions,
|
2021-11-21 17:30:24 +00:00
|
|
|
#[builder(default = r#"String::from("http://localhost")"#)]
|
|
|
|
pub http_url: String,
|
2021-06-14 14:02:36 +00:00
|
|
|
#[serde(skip)]
|
2021-11-27 15:34:50 +00:00
|
|
|
#[builder(field(private), default = "None")]
|
2021-06-23 18:33:36 +00:00
|
|
|
server_setup: Option<ServerSetup>,
|
2021-03-02 19:30:43 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 08:09:19 +00:00
|
|
|
impl std::default::Default for Configuration {
|
|
|
|
fn default() -> Self {
|
|
|
|
ConfigurationBuilder::default().build().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
impl ConfigurationBuilder {
|
|
|
|
pub fn build(self) -> Result<Configuration> {
|
2021-06-23 18:33:36 +00:00
|
|
|
let server_setup = get_server_setup(self.key_file.as_deref().unwrap_or("server_key"))?;
|
2021-11-27 15:34:50 +00:00
|
|
|
Ok(self.server_setup(Some(server_setup)).private_build()?)
|
2021-03-02 19:30:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 20:43:26 +00:00
|
|
|
impl Configuration {
|
2021-06-23 18:33:36 +00:00
|
|
|
pub fn get_server_setup(&self) -> &ServerSetup {
|
|
|
|
self.server_setup.as_ref().unwrap()
|
|
|
|
}
|
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
pub fn get_server_keys(&self) -> &KeyPair {
|
2021-06-23 18:33:36 +00:00
|
|
|
self.get_server_setup().keypair()
|
2021-06-14 14:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 18:33:36 +00:00
|
|
|
fn get_server_setup(file_path: &str) -> Result<ServerSetup> {
|
2021-06-14 14:02:36 +00:00
|
|
|
use std::path::Path;
|
|
|
|
let path = Path::new(file_path);
|
|
|
|
if path.exists() {
|
2021-08-26 19:56:42 +00:00
|
|
|
let bytes =
|
|
|
|
std::fs::read(file_path).context(format!("Could not read key file `{}`", file_path))?;
|
2021-06-23 18:33:36 +00:00
|
|
|
Ok(ServerSetup::deserialize(&bytes)?)
|
2021-06-14 14:02:36 +00:00
|
|
|
} else {
|
|
|
|
let mut rng = rand::rngs::OsRng;
|
2021-06-23 18:33:36 +00:00
|
|
|
let server_setup = ServerSetup::new(&mut rng);
|
2021-08-26 19:56:42 +00:00
|
|
|
std::fs::write(path, server_setup.serialize()).context(format!(
|
|
|
|
"Could not write the generated server setup to file `{}`",
|
|
|
|
file_path,
|
|
|
|
))?;
|
2021-06-23 18:33:36 +00:00
|
|
|
Ok(server_setup)
|
2021-06-14 14:02:36 +00:00
|
|
|
}
|
2021-03-02 20:43:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
pub trait ConfigOverrider {
|
|
|
|
fn override_config(&self, config: &mut Configuration);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait TopLevelCommandOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopLevelCommandOpts for RunOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts {
|
|
|
|
&self.general_config
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopLevelCommandOpts for TestEmailOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts {
|
|
|
|
&self.general_config
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for RunOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
self.general_config.override_config(config);
|
2021-11-27 15:34:50 +00:00
|
|
|
|
|
|
|
if let Some(path) = self.server_key_file.as_ref() {
|
|
|
|
config.key_file = path.to_string();
|
|
|
|
}
|
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
if let Some(port) = self.ldap_port {
|
|
|
|
config.ldap_port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(port) = self.http_port {
|
|
|
|
config.http_port = port;
|
|
|
|
}
|
2021-11-21 17:30:24 +00:00
|
|
|
|
|
|
|
if let Some(url) = self.http_url.as_ref() {
|
|
|
|
config.http_url = url.to_string();
|
|
|
|
}
|
2021-11-11 09:14:03 +00:00
|
|
|
self.smtp_opts.override_config(config);
|
2022-05-05 13:53:58 +00:00
|
|
|
self.ldaps_opts.override_config(config);
|
2021-11-11 09:14:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for TestEmailOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
self.general_config.override_config(config);
|
|
|
|
self.smtp_opts.override_config(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 13:53:58 +00:00
|
|
|
impl ConfigOverrider for LdapsOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
if let Some(enabled) = self.ldaps_enabled {
|
|
|
|
config.ldaps_options.enabled = enabled;
|
|
|
|
}
|
|
|
|
if let Some(port) = self.ldaps_port {
|
|
|
|
config.ldaps_options.port = port;
|
|
|
|
}
|
|
|
|
if let Some(path) = self.ldaps_cert_file.as_ref() {
|
|
|
|
config.ldaps_options.cert_file = path.clone();
|
|
|
|
}
|
|
|
|
if let Some(path) = self.ldaps_key_file.as_ref() {
|
|
|
|
config.ldaps_options.key_file = path.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
impl ConfigOverrider for GeneralConfigOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
if self.verbose {
|
|
|
|
config.verbose = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for SmtpOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
if let Some(from) = &self.smtp_from {
|
|
|
|
config.smtp_options.from = Some(from.clone());
|
|
|
|
}
|
|
|
|
if let Some(reply_to) = &self.smtp_reply_to {
|
|
|
|
config.smtp_options.reply_to = Some(reply_to.clone());
|
|
|
|
}
|
|
|
|
if let Some(server) = &self.smtp_server {
|
|
|
|
config.smtp_options.server = server.clone();
|
|
|
|
}
|
|
|
|
if let Some(port) = self.smtp_port {
|
|
|
|
config.smtp_options.port = port;
|
|
|
|
}
|
|
|
|
if let Some(user) = &self.smtp_user {
|
|
|
|
config.smtp_options.user = user.clone();
|
|
|
|
}
|
|
|
|
if let Some(password) = &self.smtp_password {
|
2021-11-11 09:36:42 +00:00
|
|
|
config.smtp_options.password = SecUtf8::from(password.clone());
|
2021-11-11 09:14:03 +00:00
|
|
|
}
|
|
|
|
if let Some(tls_required) = self.smtp_tls_required {
|
|
|
|
config.smtp_options.tls_required = tls_required;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn init<C>(overrides: C) -> Result<Configuration>
|
|
|
|
where
|
|
|
|
C: TopLevelCommandOpts + ConfigOverrider,
|
|
|
|
{
|
|
|
|
let config_file = overrides.general_config().config_file.clone();
|
2021-03-02 20:43:26 +00:00
|
|
|
|
2021-11-09 09:29:46 +00:00
|
|
|
println!(
|
|
|
|
"Loading configuration from {}",
|
2021-11-11 09:14:03 +00:00
|
|
|
overrides.general_config().config_file
|
2021-11-09 09:29:46 +00:00
|
|
|
);
|
2021-10-20 06:05:26 +00:00
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
let mut config: Configuration = Figment::from(Serialized::defaults(
|
2021-11-27 15:34:50 +00:00
|
|
|
ConfigurationBuilder::default().private_build().unwrap(),
|
2021-11-03 07:01:34 +00:00
|
|
|
))
|
|
|
|
.merge(Toml::file(config_file))
|
2021-11-03 08:09:19 +00:00
|
|
|
.merge(Env::prefixed("LLDAP_").split("__"))
|
2021-11-03 07:01:34 +00:00
|
|
|
.extract()?;
|
2021-03-02 19:30:43 +00:00
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
overrides.override_config(&mut config);
|
|
|
|
if config.verbose {
|
|
|
|
println!("Configuration: {:#?}", &config);
|
|
|
|
}
|
2021-06-23 18:33:36 +00:00
|
|
|
config.server_setup = Some(get_server_setup(&config.key_file)?);
|
2021-11-11 09:36:42 +00:00
|
|
|
if config.jwt_secret == SecUtf8::from("secretjwtsecret") {
|
2021-11-03 07:01:34 +00:00
|
|
|
println!("WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.");
|
|
|
|
}
|
2021-11-11 09:36:42 +00:00
|
|
|
if config.ldap_user_pass == SecUtf8::from("password") {
|
2021-11-03 07:01:34 +00:00
|
|
|
println!("WARNING: Unsecure default admin password is used.");
|
|
|
|
}
|
2021-03-02 19:30:43 +00:00
|
|
|
Ok(config)
|
2021-03-02 19:13:58 +00:00
|
|
|
}
|