From 91de1137ea6d313f398c695630cb7c05d1c83233 Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 16:58:37 +0200 Subject: [PATCH 1/6] Convert params from owned to borrowed --- sqlx-postgres/src/options/pgpass.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sqlx-postgres/src/options/pgpass.rs b/sqlx-postgres/src/options/pgpass.rs index bf16559548..e80cf495e1 100644 --- a/sqlx-postgres/src/options/pgpass.rs +++ b/sqlx-postgres/src/options/pgpass.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::env::var_os; use std::fs::File; use std::io::{BufRead, BufReader}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// try to load a password from the various pgpass file locations pub fn load_password( @@ -14,7 +14,7 @@ pub fn load_password( let custom_file = var_os("PGPASSFILE"); if let Some(file) = custom_file { if let Some(password) = - load_password_from_file(PathBuf::from(file), host, port, username, database) + load_password_from_file(&PathBuf::from(file), host, port, username, database) { return Some(password); } @@ -30,18 +30,18 @@ pub fn load_password( .ok() .map(|basedirs| basedirs.data_dir().join("postgres").join("pgpass.conf")) }; - load_password_from_file(default_file?, host, port, username, database) + load_password_from_file(&default_file?, host, port, username, database) } /// try to extract a password from a pgpass file fn load_password_from_file( - path: PathBuf, + path: &Path, host: &str, port: u16, username: &str, database: Option<&str>, ) -> Option { - let file = File::open(&path) + let file = File::open(path) .map_err(|e| { match e.kind() { std::io::ErrorKind::NotFound => { From e21ba020b56bcac07ece60ce15cb1abd07a21b96 Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 17:08:15 +0200 Subject: [PATCH 2/6] Factor out default_path function --- sqlx-postgres/src/options/pgpass.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sqlx-postgres/src/options/pgpass.rs b/sqlx-postgres/src/options/pgpass.rs index e80cf495e1..952a88a87d 100644 --- a/sqlx-postgres/src/options/pgpass.rs +++ b/sqlx-postgres/src/options/pgpass.rs @@ -20,17 +20,21 @@ pub fn load_password( } } - #[cfg(not(target_os = "windows"))] - let default_file = home::home_dir().map(|path| path.join(".pgpass")); - #[cfg(target_os = "windows")] - let default_file = { - use etcetera::BaseStrategy; - - etcetera::base_strategy::Windows::new() - .ok() - .map(|basedirs| basedirs.data_dir().join("postgres").join("pgpass.conf")) - }; - load_password_from_file(&default_file?, host, port, username, database) + load_password_from_file(&default_path()?, host, port, username, database) +} + +#[cfg(not(target_os = "windows"))] +fn default_path() -> Option { + home::home_dir().map(|path| path.join(".pgpass")) +} + +#[cfg(target_os = "windows")] +fn default_path() -> Option { + use etcetera::BaseStrategy; + + etcetera::base_strategy::Windows::new() + .ok() + .map(|basedirs| basedirs.data_dir().join("postgres").join("pgpass.conf")) } /// try to extract a password from a pgpass file From dc088b37f8b1b20ec47b96d8379e8fa276822ba1 Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 17:14:46 +0200 Subject: [PATCH 3/6] Enable loading pgpass from custom paths --- sqlx-postgres/src/options/mod.rs | 1 + sqlx-postgres/src/options/pgpass.rs | 31 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index efbc43989b..7cfca1b8bf 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -100,6 +100,7 @@ impl PgConnectOptions { self.port, &self.username, self.database.as_deref(), + &[] as &[&Path], ); } diff --git a/sqlx-postgres/src/options/pgpass.rs b/sqlx-postgres/src/options/pgpass.rs index 952a88a87d..6afc6a7228 100644 --- a/sqlx-postgres/src/options/pgpass.rs +++ b/sqlx-postgres/src/options/pgpass.rs @@ -4,23 +4,32 @@ use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; -/// try to load a password from the various pgpass file locations +/// Try to load a password from the various pgpass file locations. +/// +/// Loading is attempted in the following order: +/// 1. Path given via the `PGPASSFILE` environment variable. +/// 2. Paths given via custom_paths. +/// 3. Default path (`~/.pgpass` on Linux and `%APPDATA%/postgres/pgpass.conf` +/// on Windows) pub fn load_password( host: &str, port: u16, username: &str, database: Option<&str>, + custom_paths: &[impl AsRef], ) -> Option { - let custom_file = var_os("PGPASSFILE"); - if let Some(file) = custom_file { - if let Some(password) = - load_password_from_file(&PathBuf::from(file), host, port, username, database) - { - return Some(password); - } - } - - load_password_from_file(&default_path()?, host, port, username, database) + let env_path = var_os("PGPASSFILE").map(PathBuf::from); + let default_path = default_path(); + + let path_iter = env_path + .as_deref() + .into_iter() + .chain(custom_paths.iter().map(AsRef::as_ref)) + .chain(default_path.as_deref()); + + path_iter + .filter_map(|path| load_password_from_file(path, host, port, username, database)) + .next() } #[cfg(not(target_os = "windows"))] From 39b2f67675dca638fc4a1b3e23fa93451dcf0015 Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 17:39:58 +0200 Subject: [PATCH 4/6] Store custom passfile paths for PgConnectOptions --- sqlx-postgres/src/options/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index 7cfca1b8bf..bd94c7618b 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -20,6 +20,7 @@ pub struct PgConnectOptions { pub(crate) socket: Option, pub(crate) username: String, pub(crate) password: Option, + pub(crate) passfile_paths: Vec, pub(crate) database: Option, pub(crate) ssl_mode: PgSslMode, pub(crate) ssl_root_cert: Option, @@ -74,6 +75,7 @@ impl PgConnectOptions { socket: None, username, password: var("PGPASSWORD").ok(), + passfile_paths: vec![], database, ssl_root_cert: var("PGSSLROOTCERT").ok().map(CertificateInput::from), ssl_client_cert: var("PGSSLCERT").ok().map(CertificateInput::from), @@ -100,7 +102,7 @@ impl PgConnectOptions { self.port, &self.username, self.database.as_deref(), - &[] as &[&Path], + &self.passfile_paths, ); } From d8f8fadab095b9b6fa729811028837a75cfdd25f Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 17:41:16 +0200 Subject: [PATCH 5/6] Add passfile_paths setter method --- sqlx-postgres/src/options/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index bd94c7618b..e77f0798ed 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::env::var; +use std::ffi::OsStr; use std::fmt::{self, Display, Write}; use std::path::{Path, PathBuf}; @@ -187,6 +188,23 @@ impl PgConnectOptions { self } + /// Sets the paths to try when looking for the pgpass file. + /// + /// # Example + /// + /// ```rust + /// # use sqlx_postgres::PgConnectOptions; + /// let options = PgConnectOptions::new() + /// .passfile_paths(&["/non/default/pgpass"]); + /// ``` + pub fn passfile_paths

(mut self, paths: impl IntoIterator) -> Self + where + P: Into + AsRef, + { + self.passfile_paths = paths.into_iter().map(Into::into).collect(); + self + } + /// Sets the database name. Defaults to be the same as the user name. /// /// # Example From ffa2be6da3362cea36830a4e93f0c1d6f464c218 Mon Sep 17 00:00:00 2001 From: "Kai A. Hiller" Date: Tue, 27 May 2025 17:42:30 +0200 Subject: [PATCH 6/6] Accept passfile connection option --- sqlx-postgres/src/options/parse.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sqlx-postgres/src/options/parse.rs b/sqlx-postgres/src/options/parse.rs index e911305698..54e82420a0 100644 --- a/sqlx-postgres/src/options/parse.rs +++ b/sqlx-postgres/src/options/parse.rs @@ -3,6 +3,7 @@ use crate::{PgConnectOptions, PgSslMode}; use sqlx_core::percent_encoding::{percent_decode_str, utf8_percent_encode, NON_ALPHANUMERIC}; use sqlx_core::Url; use std::net::IpAddr; +use std::path::PathBuf; use std::str::FromStr; impl PgConnectOptions { @@ -87,6 +88,8 @@ impl PgConnectOptions { "password" => options = options.password(&value), + "passfile" => options.passfile_paths.insert(0, PathBuf::from(&*value)), + "application_name" => options = options.application_name(&value), "options" => { @@ -242,6 +245,20 @@ fn it_parses_password_correctly_from_parameter() { assert_eq!(Some("some_pass"), opts.password.as_deref()); } +#[test] +fn it_parses_passfile_correctly_from_parameter() { + let url = "postgres:///?passfile=/non%20default/pgpass&passfile=.pgpass"; + let opts = PgConnectOptions::from_str(url).unwrap(); + + assert_eq!( + vec![ + PathBuf::from(".pgpass"), + PathBuf::from("/non default/pgpass"), + ], + opts.passfile_paths + ); +} + #[test] fn it_parses_application_name_correctly_from_parameter() { let url = "postgres:///?application_name=some_name";