server: Treat the database password as a secret

This commit is contained in:
Valentin Tolmer 2024-01-22 23:02:30 +01:00 committed by nitnelave
parent addd453287
commit b82a2d5705
5 changed files with 69 additions and 8 deletions

View File

@ -3,6 +3,8 @@ use lettre::message::Mailbox;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::infra::database_string::DatabaseUrl;
/// lldap is a lightweight LDAP server
#[derive(Debug, Parser, Clone)]
#[clap(version, author)]
@ -87,7 +89,7 @@ pub struct RunOpts {
/// Database connection URL
#[clap(short, long, env = "LLDAP_DATABASE_URL")]
pub database_url: Option<String>,
pub database_url: Option<DatabaseUrl>,
/// Force admin password reset to the config value.
#[clap(long, env = "LLDAP_FORCE_LADP_USER_PASS_RESET")]

View File

@ -3,7 +3,10 @@ use crate::{
sql_tables::{ConfigLocation, PrivateKeyHash, PrivateKeyInfo, PrivateKeyLocation},
types::{AttributeName, UserId},
},
infra::cli::{GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts},
infra::{
cli::{GeneralConfigOpts, LdapsOpts, RunOpts, SmtpEncryption, SmtpOpts, TestEmailOpts},
database_string::DatabaseUrl,
},
};
use anyhow::{bail, Context, Result};
use figment::{
@ -91,8 +94,8 @@ pub struct Configuration {
pub force_ldap_user_pass_reset: bool,
#[builder(default = "false")]
pub force_update_private_key: bool,
#[builder(default = r#"String::from("sqlite://users.db?mode=rwc")"#)]
pub database_url: String,
#[builder(default = r#"DatabaseUrl::from("sqlite://users.db?mode=rwc")"#)]
pub database_url: DatabaseUrl,
#[builder(default)]
pub ignored_user_attributes: Vec<AttributeName>,
#[builder(default)]
@ -411,7 +414,7 @@ impl ConfigOverrider for RunOpts {
}
if let Some(database_url) = self.database_url.as_ref() {
config.database_url = database_url.to_string();
config.database_url = database_url.clone();
}
if let Some(force_ldap_user_pass_reset) = self.force_ldap_user_pass_reset {

View File

@ -0,0 +1,54 @@
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Clone, Serialize, Deserialize)]
pub struct DatabaseUrl(Url);
impl From<Url> for DatabaseUrl {
fn from(url: Url) -> Self {
Self(url)
}
}
impl From<&str> for DatabaseUrl {
fn from(url: &str) -> Self {
Self(Url::parse(url).expect("Invalid database URL"))
}
}
impl std::fmt::Debug for DatabaseUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.password().is_some() {
let mut url = self.0.clone();
// It can fail for URLs that cannot have a password, like "mailto:bob@example".
let _ = url.set_password(Some("***PASSWORD***"));
f.write_fmt(format_args!("{}", url))
} else {
f.write_fmt(format_args!("{}", self.0))
}
}
}
impl ToString for DatabaseUrl {
fn to_string(&self) -> String {
self.0.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_database_url_debug() {
let url = DatabaseUrl::from("postgres://user:pass@localhost:5432/dbname");
assert_eq!(
format!("{:?}", url),
"postgres://user:***PASSWORD***@localhost:5432/dbname"
);
assert_eq!(
url.to_string(),
"postgres://user:pass@localhost:5432/dbname"
);
}
}

View File

@ -2,6 +2,7 @@ pub mod access_control;
pub mod auth_service;
pub mod cli;
pub mod configuration;
pub mod database_string;
pub mod db_cleaner;
pub mod graphql;
pub mod healthcheck;

View File

@ -18,6 +18,7 @@ use crate::{
infra::{
cli::*,
configuration::{compare_private_key_hashes, Configuration},
database_string::DatabaseUrl,
db_cleaner::Scheduler,
healthcheck, mail,
},
@ -84,7 +85,7 @@ async fn set_up_server(config: Configuration) -> Result<ServerBuilder> {
info!("Starting LLDAP version {}", env!("CARGO_PKG_VERSION"));
let sql_pool = {
let mut sql_opt = sea_orm::ConnectOptions::new(config.database_url.clone());
let mut sql_opt = sea_orm::ConnectOptions::new(config.database_url.to_string());
sql_opt
.max_connections(5)
.sqlx_logging(true)
@ -248,9 +249,9 @@ fn run_healthcheck(opts: RunOpts) -> Result<()> {
std::process::exit(i32::from(failure))
}
async fn create_schema(database_url: String) -> Result<()> {
async fn create_schema(database_url: DatabaseUrl) -> Result<()> {
let sql_pool = {
let mut sql_opt = sea_orm::ConnectOptions::new(database_url.clone());
let mut sql_opt = sea_orm::ConnectOptions::new(database_url.to_string());
sql_opt
.max_connections(1)
.sqlx_logging(true)