From 67e51f8a8bbc02eda606bccd98be550b1dbb379a Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sat, 5 Oct 2024 23:07:40 +0200 Subject: [PATCH] new database_password configuration option --- CHANGELOG.md | 1 + configuration.md | 8 +++++++- src/app_config.rs | 3 +++ src/webserver/database/connect.rs | 19 ++++++++++++++++++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4e1bd6..39913d15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - **Fix**: the search feature in the shell component was not working when no menu item was defined. - Add support for encrypted Microsoft SQL Server connections. This finally allows connecting to databases that refuse clear-text connections, such as those hosted on Azure. - Easier json handling in databases without a native json type. SQLPage now detects when you use a json function in SQLite or MariaDB to generate a column, and automatically converts the resulting string to a json object. This allows easily using components that take json parameters (like the new columns component) in MariaDB and SQLite. + - Add a new optional `database_password` configuration option to set the password for the database connection separately from the connection string. This allows to keep the password separate from the connection string, which can be useful for security purposes, logging, and avoids having to percent-encode the password in the connection string. ## 0.29.0 (2024-09-25) - New columns component: `columns`. Useful to display a comparison between items, or large key figures to an user. diff --git a/configuration.md b/configuration.md index bcefbc64..e0ce3847 100644 --- a/configuration.md +++ b/configuration.md @@ -10,6 +10,7 @@ Here are the available configuration options and their default values: | --------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `listen_on` | 0.0.0.0:8080 | Interface and port on which the web server should listen | | `database_url` | sqlite://sqlpage.db?mode=rwc | Database connection URL, in the form `dbengine://user:password@host:port/dbname`. Special characters in user and password should be [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding). | +| `database_password` | | Database password. If set, this will override any password specified in the `database_url`. This allows you to keep the password separate from the connection string for better security. | | `port` | 8080 | Like listen_on, but specifies only the port. | | `unix_socket` | | Path to a UNIX socket to listen on instead of the TCP port. If specified, SQLPage will accept HTTP connections only on this socket and not on any TCP port. This option is mutually exclusive with `listen_on` and `port`. | `max_database_pool_connections` | PostgreSQL: 50
MySql: 75
SQLite: 16
MSSQL: 100 | How many simultaneous database connections to open at most | @@ -75,10 +76,15 @@ A full connection string for a PostgreSQL database might look like this: postgres://my_user:p%40ss@localhost:5432/my_database?sslmode=verify-ca&sslrootcert=/path/to/ca.pem&sslcert=/path/to/cert.pem&sslkey=/path/to/key.pem&application_name=my_application ``` +If the `database_password` configuration parameter is set, it will override any password specified in the `database_url`. +It does not need to be percent-encoded. +This allows you to keep the password separate from the connection string, which can be useful for security purposes, especially when storing configurations in version control systems. + ### Example `.env` file ```bash -DATABASE_URL="sqlite:///path/to/my_database.db?mode=rwc" +DATABASE_URL="postgres://my_user@localhost:5432/my_database?sslmode=verify-ca&sslrootcert=/path/to/ca.pem" +DATABASE_PASSWORD="my_secure_password" SQLITE_EXTENSIONS="mod_spatialite crypto define regexp" ``` diff --git a/src/app_config.rs b/src/app_config.rs index fb57248c..4e7265c6 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -148,6 +148,9 @@ pub fn load_from_env() -> anyhow::Result { pub struct AppConfig { #[serde(default = "default_database_url")] pub database_url: String, + /// A separate field for the database password. If set, this will override any password specified in the `database_url`. + #[serde(default)] + pub database_password: Option, pub max_database_pool_connections: Option, pub database_connection_idle_timeout_seconds: Option, pub database_connection_max_lifetime_seconds: Option, diff --git a/src/webserver/database/connect.rs b/src/webserver/database/connect.rs index 01f81ba6..12966653 100644 --- a/src/webserver/database/connect.rs +++ b/src/webserver/database/connect.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{mem::take, time::Duration}; use super::Database; use crate::{app_config::AppConfig, ON_CONNECT_FILE}; @@ -16,6 +16,9 @@ impl Database { let mut connect_options: AnyConnectOptions = database_url .parse() .with_context(|| format!("\"{database_url}\" is not a valid database URL. Please change the \"database_url\" option in the configuration file."))?; + if let Some(password) = &config.database_password { + set_database_password(&mut connect_options, password); + } connect_options.log_statements(log::LevelFilter::Trace); connect_options.log_slow_statements( log::LevelFilter::Warn, @@ -148,3 +151,17 @@ fn make_sqlite_fun(name: &str, f: fn(&str) -> String) -> Function { } }) } + +fn set_database_password(options: &mut AnyConnectOptions, password: &str) { + if let Some(opts) = options.as_postgres_mut() { + *opts = take(opts).password(password); + } else if let Some(opts) = options.as_mysql_mut() { + *opts = take(opts).password(password); + } else if let Some(opts) = options.as_mssql_mut() { + *opts = take(opts).password(password); + } else if let Some(_opts) = options.as_sqlite_mut() { + log::warn!("Setting a password for a SQLite database is not supported"); + } else { + unreachable!("Unsupported database type"); + } +}