Skip to content

Commit 367a463

Browse files
committed
fix: psgl require ssl in config
1 parent 41a718d commit 367a463

2 files changed

Lines changed: 111 additions & 41 deletions

File tree

crates/cdk-mintd/src/lib.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ use cdk_common::database::DynMintDatabase;
4545
use cdk_common::payment::MetricsMintPayment;
4646
use cdk_common::payment::MintPayment;
4747
#[cfg(feature = "postgres")]
48-
use cdk_postgres::MintPgAuthDatabase;
49-
#[cfg(feature = "postgres")]
50-
use cdk_postgres::MintPgDatabase;
48+
use cdk_postgres::{MintPgAuthDatabase, MintPgDatabase, PgConfig};
5149
#[cfg(feature = "sqlite")]
5250
use cdk_sqlite::mint::MintSqliteAuthDatabase;
5351
#[cfg(feature = "sqlite")]
@@ -305,7 +303,14 @@ async fn setup_database(
305303
}
306304

307305
#[cfg(feature = "postgres")]
308-
let pg_db = Arc::new(MintPgDatabase::new(pg_config.url.as_str()).await?);
306+
let db_config = PgConfig::new(
307+
pg_config.url.as_str(),
308+
pg_config.tls_mode.as_deref(),
309+
pg_config.max_connections,
310+
pg_config.connection_timeout_seconds,
311+
);
312+
#[cfg(feature = "postgres")]
313+
let pg_db = Arc::new(MintPgDatabase::new(db_config).await?);
309314
tracing::info!("PostgreSQL database connection established");
310315
#[cfg(feature = "postgres")]
311316
let localstore: Arc<dyn MintDatabase<cdk_database::Error> + Send + Sync> =
@@ -779,7 +784,13 @@ async fn setup_authentication(
779784
bail!("Auth database PostgreSQL URL is required and cannot be empty. Set it in config file [auth_database.postgres] section or via CDK_MINTD_AUTH_POSTGRES_URL environment variable");
780785
}
781786

782-
Arc::new(MintPgAuthDatabase::new(auth_pg_config.url.as_str()).await?)
787+
let auth_db_config = PgConfig::new(
788+
auth_pg_config.url.as_str(),
789+
auth_pg_config.tls_mode.as_deref(),
790+
auth_pg_config.max_connections,
791+
auth_pg_config.connection_timeout_seconds,
792+
);
793+
Arc::new(MintPgAuthDatabase::new(auth_db_config).await?)
783794
}
784795
#[cfg(not(feature = "postgres"))]
785796
{

crates/cdk-postgres/src/lib.rs

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,108 @@ pub struct PgConfig {
6262
url: String,
6363
schema: Option<String>,
6464
tls: SslMode,
65+
max_connections: usize,
66+
connection_timeout: Duration,
6567
}
6668

6769
impl DatabaseConfig for PgConfig {
6870
fn default_timeout(&self) -> Duration {
69-
Duration::from_secs(10)
71+
self.connection_timeout
7072
}
7173

7274
fn max_size(&self) -> usize {
73-
20
75+
self.max_connections
76+
}
77+
}
78+
79+
/// Default maximum number of connections in the pool
80+
const DEFAULT_MAX_CONNECTIONS: usize = 20;
81+
82+
/// Default connection timeout in seconds
83+
const DEFAULT_CONNECTION_TIMEOUT_SECS: u64 = 10;
84+
85+
/// Build a TLS connector with the given certificate/hostname validation settings.
86+
fn build_tls(accept_invalid_certs: bool, accept_invalid_hostnames: bool) -> SslMode {
87+
let mut builder = TlsConnector::builder();
88+
if accept_invalid_certs {
89+
builder.danger_accept_invalid_certs(true);
90+
}
91+
if accept_invalid_hostnames {
92+
builder.danger_accept_invalid_hostnames(true);
93+
}
94+
95+
match builder.build() {
96+
Ok(connector) => {
97+
let make_tls_connector = MakeTlsConnector::new(connector);
98+
SslMode::NativeTls(make_tls_connector)
99+
}
100+
Err(_) => SslMode::NoTls(NoTls {}),
101+
}
102+
}
103+
104+
/// Determine TLS mode from the `sslmode=` parameter in a connection URL.
105+
fn ssl_mode_from_url(url: &str) -> SslMode {
106+
if url.contains(SSLMODE_VERIFY_FULL) {
107+
// Strict TLS: valid certs and hostnames required
108+
build_tls(false, false)
109+
} else if url.contains(SSLMODE_VERIFY_CA) {
110+
// Verify CA, but allow invalid hostnames
111+
build_tls(false, true)
112+
} else if url.contains(SSLMODE_PREFER)
113+
|| url.contains(SSLMODE_ALLOW)
114+
|| url.contains(SSLMODE_REQUIRE)
115+
{
116+
// Lenient TLS for preferred/allow/require: accept invalid certs and hostnames
117+
build_tls(true, true)
118+
} else {
119+
SslMode::NoTls(NoTls {})
120+
}
121+
}
122+
123+
/// Resolve TLS mode from an explicit `tls_mode` string (from config/env), such
124+
/// as `"disable"`, `"prefer"`, `"require"`, `"verify-ca"`, or `"verify-full"`.
125+
///
126+
/// If the value is `None`, falls back to parsing `sslmode=` from the URL.
127+
fn ssl_mode_from_config(tls_mode: Option<&str>, url: &str) -> SslMode {
128+
match tls_mode {
129+
Some(mode) => match mode.to_lowercase().as_str() {
130+
"verify-full" => build_tls(false, false),
131+
"verify-ca" => build_tls(false, true),
132+
"require" | "prefer" | "allow" => build_tls(true, true),
133+
// "disable" or any unrecognised value → no TLS
134+
_ => SslMode::NoTls(NoTls {}),
135+
},
136+
// No explicit tls_mode: fall back to URL-based detection
137+
None => ssl_mode_from_url(url),
74138
}
75139
}
76140

77141
impl PgConfig {
142+
/// Create a new `PgConfig` with explicit TLS mode, pool size, and timeout.
143+
///
144+
/// `tls_mode` accepts the same strings as the configuration file:
145+
/// `"disable"`, `"prefer"`, `"allow"`, `"require"`, `"verify-ca"`,
146+
/// `"verify-full"`. When `None`, the TLS mode is inferred from
147+
/// `sslmode=` in the connection URL (matching the old behaviour).
148+
pub fn new(
149+
conn_str: &str,
150+
tls_mode: Option<&str>,
151+
max_connections: Option<usize>,
152+
connection_timeout_secs: Option<u64>,
153+
) -> Self {
154+
let (schema, conn_str) = Self::strip_schema(conn_str);
155+
let tls = ssl_mode_from_config(tls_mode, &conn_str);
156+
PgConfig {
157+
url: conn_str,
158+
schema,
159+
tls,
160+
max_connections: max_connections.unwrap_or(DEFAULT_MAX_CONNECTIONS),
161+
connection_timeout: Duration::from_secs(
162+
connection_timeout_secs.unwrap_or(DEFAULT_CONNECTION_TIMEOUT_SECS),
163+
),
164+
}
165+
}
166+
78167
/// strip schema from the connection string
79168
fn strip_schema(input: &str) -> (Option<String>, String) {
80169
let mut schema: Option<String> = None;
@@ -97,44 +186,14 @@ impl PgConfig {
97186
impl From<&str> for PgConfig {
98187
fn from(conn_str: &str) -> Self {
99188
let (schema, conn_str) = Self::strip_schema(conn_str);
100-
fn build_tls(accept_invalid_certs: bool, accept_invalid_hostnames: bool) -> SslMode {
101-
let mut builder = TlsConnector::builder();
102-
if accept_invalid_certs {
103-
builder.danger_accept_invalid_certs(true);
104-
}
105-
if accept_invalid_hostnames {
106-
builder.danger_accept_invalid_hostnames(true);
107-
}
108-
109-
match builder.build() {
110-
Ok(connector) => {
111-
let make_tls_connector = MakeTlsConnector::new(connector);
112-
SslMode::NativeTls(make_tls_connector)
113-
}
114-
Err(_) => SslMode::NoTls(NoTls {}),
115-
}
116-
}
117-
118-
let tls = if conn_str.contains(SSLMODE_VERIFY_FULL) {
119-
// Strict TLS: valid certs and hostnames required
120-
build_tls(false, false)
121-
} else if conn_str.contains(SSLMODE_VERIFY_CA) {
122-
// Verify CA, but allow invalid hostnames
123-
build_tls(false, true)
124-
} else if conn_str.contains(SSLMODE_PREFER)
125-
|| conn_str.contains(SSLMODE_ALLOW)
126-
|| conn_str.contains(SSLMODE_REQUIRE)
127-
{
128-
// Lenient TLS for preferred/allow/require: accept invalid certs and hostnames
129-
build_tls(true, true)
130-
} else {
131-
SslMode::NoTls(NoTls {})
132-
};
189+
let tls = ssl_mode_from_url(&conn_str);
133190

134191
PgConfig {
135-
url: conn_str.to_owned(),
192+
url: conn_str,
136193
schema,
137194
tls,
195+
max_connections: DEFAULT_MAX_CONNECTIONS,
196+
connection_timeout: Duration::from_secs(DEFAULT_CONNECTION_TIMEOUT_SECS),
138197
}
139198
}
140199
}

0 commit comments

Comments
 (0)