mirror of
https://codeberg.org/icewind/nextcloud-config-parser.git
synced 2026-06-03 16:44:09 +02:00
better handling of postgres options
This commit is contained in:
parent
484af6a8e7
commit
a8f4622c1a
5 changed files with 86 additions and 37 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -461,13 +461,19 @@ dependencies = [
|
||||||
"foldhash",
|
"foldhash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -650,12 +656,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.16.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -839,6 +845,7 @@ name = "nextcloud-config-parser"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
|
"indexmap",
|
||||||
"itertools",
|
"itertools",
|
||||||
"miette",
|
"miette",
|
||||||
"php-literal-parser",
|
"php-literal-parser",
|
||||||
|
|
@ -1393,7 +1400,7 @@ dependencies = [
|
||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hashbrown",
|
"hashbrown 0.15.2",
|
||||||
"hashlink",
|
"hashlink",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ miette = "7.4.0"
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
form_urlencoded = "1.2.1"
|
form_urlencoded = "1.2.1"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
indexmap = "2.13.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
miette = { version = "7.4.0", features = ["fancy"] }
|
miette = { version = "7.4.0", features = ["fancy"] }
|
||||||
|
|
|
||||||
25
src/lib.rs
25
src/lib.rs
|
|
@ -1,6 +1,7 @@
|
||||||
mod nc;
|
mod nc;
|
||||||
|
|
||||||
use form_urlencoded::Serializer;
|
use form_urlencoded::Serializer;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
|
@ -192,7 +193,7 @@ pub enum NotAConfigError {
|
||||||
NotAnArray(PathBuf),
|
NotAnArray(PathBuf),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum SslOptions {
|
pub enum SslOptions {
|
||||||
Enabled {
|
Enabled {
|
||||||
key: String,
|
key: String,
|
||||||
|
|
@ -204,7 +205,7 @@ pub enum SslOptions {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Database {
|
pub enum Database {
|
||||||
Sqlite {
|
Sqlite {
|
||||||
database: PathBuf,
|
database: PathBuf,
|
||||||
|
|
@ -221,11 +222,11 @@ pub enum Database {
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
password: String,
|
||||||
connect: DbConnect,
|
connect: DbConnect,
|
||||||
ssl_options: SslOptions,
|
options: IndexMap<String, String>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum DbConnect {
|
pub enum DbConnect {
|
||||||
Tcp { host: String, port: u16 },
|
Tcp { host: String, port: u16 },
|
||||||
Socket(PathBuf),
|
Socket(PathBuf),
|
||||||
|
|
@ -300,21 +301,11 @@ impl Database {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
connect,
|
connect,
|
||||||
ssl_options,
|
options,
|
||||||
} => {
|
} => {
|
||||||
let mut params = Serializer::new(String::new());
|
let mut params = Serializer::new(String::new());
|
||||||
match ssl_options {
|
for (key, value) in options {
|
||||||
SslOptions::Default => {}
|
params.append_pair(key.as_str(), value.as_str());
|
||||||
SslOptions::Disabled => {
|
|
||||||
params.append_pair("sslmode", "disable");
|
|
||||||
}
|
|
||||||
SslOptions::Enabled { ca, verify, .. } => {
|
|
||||||
params.append_pair(
|
|
||||||
"ssl-mode",
|
|
||||||
if *verify { "verify-full" } else { "verify-ca" },
|
|
||||||
);
|
|
||||||
params.append_pair("sslrootcert", ca.as_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let (host, port) = match connect {
|
let (host, port) = match connect {
|
||||||
DbConnect::Socket(socket) => {
|
DbConnect::Socket(socket) => {
|
||||||
|
|
|
||||||
61
src/nc.rs
61
src/nc.rs
|
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
RedisClusterConnectionInfo, RedisConnectionInfo, RedisTlsParams, Result, SslOptions,
|
RedisClusterConnectionInfo, RedisConnectionInfo, RedisTlsParams, Result, SslOptions,
|
||||||
};
|
};
|
||||||
use crate::{RedisConfig, RedisConnectionAddr};
|
use crate::{RedisConfig, RedisConnectionAddr};
|
||||||
|
use indexmap::IndexMap;
|
||||||
use php_literal_parser::Value;
|
use php_literal_parser::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::DirEntry;
|
use std::fs::DirEntry;
|
||||||
|
|
@ -215,8 +216,10 @@ fn parse_db_options(parsed: &Value) -> Result<Database> {
|
||||||
Some("pgsql") => {
|
Some("pgsql") => {
|
||||||
let username = parsed["dbuser"].as_str().ok_or(DbError::NoUsername)?;
|
let username = parsed["dbuser"].as_str().ok_or(DbError::NoUsername)?;
|
||||||
let password = parsed["dbpassword"].as_str().unwrap_or_default();
|
let password = parsed["dbpassword"].as_str().unwrap_or_default();
|
||||||
|
let db_host = parsed["dbhost"].as_str().unwrap_or_default();
|
||||||
|
let mut host_parts = db_host.split(';');
|
||||||
let (mut connect, disable_ssl) =
|
let (mut connect, disable_ssl) =
|
||||||
match split_host(parsed["dbhost"].as_str().unwrap_or_default()) {
|
match split_host(host_parts.next().expect("empty split")) {
|
||||||
(addr, None, None) => (
|
(addr, None, None) => (
|
||||||
DbConnect::Tcp {
|
DbConnect::Tcp {
|
||||||
host: addr.into(),
|
host: addr.into(),
|
||||||
|
|
@ -248,6 +251,14 @@ fn parse_db_options(parsed: &Value) -> Result<Database> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut options = IndexMap::new();
|
||||||
|
for part in host_parts {
|
||||||
|
if let Some((key, value)) = part.split_once('=') {
|
||||||
|
options.insert(key.into(), value.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(port) = parsed["dbport"].clone().into_int() {
|
if let Some(port) = parsed["dbport"].clone().into_int() {
|
||||||
if let DbConnect::Tcp {
|
if let DbConnect::Tcp {
|
||||||
port: connect_port, ..
|
port: connect_port, ..
|
||||||
|
|
@ -256,20 +267,21 @@ fn parse_db_options(parsed: &Value) -> Result<Database> {
|
||||||
*connect_port = port as u16;
|
*connect_port = port as u16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let database = parsed["dbname"].as_str().unwrap_or("owncloud");
|
if disable_ssl {
|
||||||
|
options.insert("sslmode".into(), "disable".into());
|
||||||
|
}
|
||||||
|
|
||||||
let ssl_options = if disable_ssl {
|
let database = parsed["dbname"]
|
||||||
SslOptions::Disabled
|
.as_str()
|
||||||
} else {
|
.or_else(|| options.get("dbname").map(String::as_str))
|
||||||
SslOptions::Default
|
.unwrap_or("owncloud");
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Database::Postgres {
|
Ok(Database::Postgres {
|
||||||
database: database.into(),
|
database: database.into(),
|
||||||
username: username.into(),
|
username: username.into(),
|
||||||
password: password.into(),
|
password: password.into(),
|
||||||
connect,
|
connect,
|
||||||
ssl_options,
|
options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some("sqlite3") | Some("sqlite") | None => {
|
Some("sqlite3") | Some("sqlite") | None => {
|
||||||
|
|
@ -377,3 +389,36 @@ fn test_redis_empty_password_none() {
|
||||||
let redis = parse_redis_options(&config, "redis");
|
let redis = parse_redis_options(&config, "redis");
|
||||||
assert_eq!(redis.passwd(), None);
|
assert_eq!(redis.passwd(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_postgres_options() {
|
||||||
|
use indexmap::indexmap;
|
||||||
|
|
||||||
|
let config =
|
||||||
|
php_literal_parser::from_str(r#"[
|
||||||
|
'dbtype' => 'pgsql',
|
||||||
|
'dbhost' => 'db.example.org;sslmode=verify-ca;sslrootcert=/etc/ssl/certs/ca-certificates.crt;dbname=nextcloud',
|
||||||
|
'dbuser' => 'nextcloud',
|
||||||
|
'dbpassword' => 'nextcloud',
|
||||||
|
]"#)
|
||||||
|
.unwrap();
|
||||||
|
let db = parse_db_options(&config).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
db,
|
||||||
|
Database::Postgres {
|
||||||
|
database: "nextcloud".to_string(),
|
||||||
|
username: "nextcloud".to_string(),
|
||||||
|
password: "nextcloud".to_string(),
|
||||||
|
connect: DbConnect::Tcp {
|
||||||
|
host: "db.example.org".into(),
|
||||||
|
port: 5432,
|
||||||
|
},
|
||||||
|
options: indexmap! {
|
||||||
|
"sslmode".into() => "verify-ca".into(),
|
||||||
|
"sslrootcert".into() => "/etc/ssl/certs/ca-certificates.crt".into(),
|
||||||
|
"dbname".into() => "nextcloud".into(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(db.url(), "postgresql://nextcloud:nextcloud@db.example.org/nextcloud?sslmode=verify-ca&sslrootcert=/etc/ssl/certs/ca-certificates.crt&dbname=nextcloud");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use nextcloud_config_parser::{
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use indexmap::{indexmap, IndexMap};
|
||||||
use redis::{ConnectionAddr, ConnectionInfo};
|
use redis::{ConnectionAddr, ConnectionInfo};
|
||||||
use sqlx::mysql::{MySqlConnectOptions, MySqlSslMode};
|
use sqlx::mysql::{MySqlConnectOptions, MySqlSslMode};
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
|
|
@ -249,7 +250,7 @@ fn test_parse_postgres_socket() {
|
||||||
username: "redacted".to_string(),
|
username: "redacted".to_string(),
|
||||||
password: "redacted".to_string(),
|
password: "redacted".to_string(),
|
||||||
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
||||||
ssl_options: SslOptions::Default,
|
options: IndexMap::default(),
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -279,7 +280,7 @@ fn test_parse_postgres_socket_empty_hostname() {
|
||||||
username: "nextcloud".to_string(),
|
username: "nextcloud".to_string(),
|
||||||
password: "redacted".to_string(),
|
password: "redacted".to_string(),
|
||||||
connect: DbConnect::Socket("/run/postgresql".into()),
|
connect: DbConnect::Socket("/run/postgresql".into()),
|
||||||
ssl_options: SslOptions::Default,
|
options: IndexMap::default(),
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -309,7 +310,7 @@ fn test_parse_postgres_socket_no_pass() {
|
||||||
username: "redacted".to_string(),
|
username: "redacted".to_string(),
|
||||||
password: "".to_string(),
|
password: "".to_string(),
|
||||||
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
||||||
ssl_options: SslOptions::Default,
|
options: IndexMap::default(),
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -337,7 +338,7 @@ fn test_parse_postgres_socket_folder() {
|
||||||
username: "redacted".to_string(),
|
username: "redacted".to_string(),
|
||||||
password: "redacted".to_string(),
|
password: "redacted".to_string(),
|
||||||
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
connect: DbConnect::Socket("/var/run/postgresql".into()),
|
||||||
ssl_options: SslOptions::Default,
|
options: IndexMap::default(),
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -590,7 +591,9 @@ fn test_parse_postgres_ip() {
|
||||||
host: "1.2.3.4".to_string(),
|
host: "1.2.3.4".to_string(),
|
||||||
port: 5432,
|
port: 5432,
|
||||||
},
|
},
|
||||||
ssl_options: SslOptions::Disabled,
|
options: indexmap! {
|
||||||
|
"sslmode".into() => "disable".into()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -623,7 +626,7 @@ fn test_parse_postgres_fqdn() {
|
||||||
host: "pg.example.com".to_string(),
|
host: "pg.example.com".to_string(),
|
||||||
port: 5432,
|
port: 5432,
|
||||||
},
|
},
|
||||||
ssl_options: SslOptions::Default,
|
options: IndexMap::default(),
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
@ -691,7 +694,9 @@ fn test_parse_postgres_escaped_credentials() {
|
||||||
host: "1.2.3.4".to_string(),
|
host: "1.2.3.4".to_string(),
|
||||||
port: 5432,
|
port: 5432,
|
||||||
},
|
},
|
||||||
ssl_options: SslOptions::Disabled,
|
options: indexmap! {
|
||||||
|
"sslmode".into() => "disable".into()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&config.database,
|
&config.database,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue