mirror of
https://codeberg.org/icewind/nextcloud-config-parser.git
synced 2026-06-03 16:44:09 +02:00
fix tls redis parsing
This commit is contained in:
parent
88b64b402d
commit
83ba84fa2d
7 changed files with 137 additions and 51 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -836,7 +836,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nextcloud-config-parser"
|
name = "nextcloud-config-parser"
|
||||||
version = "0.13.0"
|
version = "0.13.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "nextcloud-config-parser"
|
name = "nextcloud-config-parser"
|
||||||
description = "Rust parser for nextcloud config files"
|
description = "Rust parser for nextcloud config files"
|
||||||
version = "0.13.0"
|
version = "0.13.1"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
|
||||||
49
src/lib.rs
49
src/lib.rs
|
|
@ -5,6 +5,7 @@ use itertools::Either;
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use nc::{parse, parse_glob};
|
pub use nc::{parse, parse_glob};
|
||||||
|
|
@ -32,12 +33,40 @@ impl RedisConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Ord, Eq)]
|
||||||
pub enum RedisConnectionAddr {
|
pub enum RedisConnectionAddr {
|
||||||
Tcp { host: String, port: u16 },
|
Tcp { host: String, port: u16, tls: bool },
|
||||||
Unix { path: PathBuf },
|
Unix { path: PathBuf },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RedisConnectionAddr {
|
||||||
|
fn parse(mut host: &str, port: Option<u16>, tls: bool) -> Self {
|
||||||
|
if host.starts_with("/") {
|
||||||
|
RedisConnectionAddr::Unix { path: host.into() }
|
||||||
|
} else {
|
||||||
|
let tls = if host.starts_with("tls://") || host.starts_with("rediss://") {
|
||||||
|
host = host.split_once("://").unwrap().1;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
tls
|
||||||
|
};
|
||||||
|
if host == "localhost" {
|
||||||
|
host = "127.0.0.1";
|
||||||
|
}
|
||||||
|
let (host, port, _) = if let Some(port) = port {
|
||||||
|
(host, Some(port), None)
|
||||||
|
} else {
|
||||||
|
split_host(host)
|
||||||
|
};
|
||||||
|
RedisConnectionAddr::Tcp {
|
||||||
|
host: host.into(),
|
||||||
|
port: port.unwrap_or(6379),
|
||||||
|
tls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RedisClusterConnectionInfo {
|
pub struct RedisClusterConnectionInfo {
|
||||||
pub addr: Vec<RedisConnectionAddr>,
|
pub addr: Vec<RedisConnectionAddr>,
|
||||||
|
|
@ -321,3 +350,19 @@ impl Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn split_host(host: &str) -> (&str, Option<u16>, Option<&str>) {
|
||||||
|
if host.starts_with('/') {
|
||||||
|
return ("localhost", None, Some(host));
|
||||||
|
}
|
||||||
|
let mut parts = host.split(':');
|
||||||
|
let host = parts.next().unwrap();
|
||||||
|
match parts
|
||||||
|
.next()
|
||||||
|
.map(|port_or_socket| u16::from_str(port_or_socket).map_err(|_| port_or_socket))
|
||||||
|
{
|
||||||
|
Some(Ok(port)) => (host, Some(port), None),
|
||||||
|
Some(Err(socket)) => (host, None, Some(socket)),
|
||||||
|
None => (host, None, None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
54
src/nc.rs
54
src/nc.rs
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Config, Database, DbConnect, DbError, Error, NotAConfigError, PhpParseError,
|
split_host, Config, Database, DbConnect, DbError, Error, NotAConfigError, PhpParseError,
|
||||||
RedisClusterConnectionInfo, RedisConnectionInfo, RedisTlsParams, Result, SslOptions,
|
RedisClusterConnectionInfo, RedisConnectionInfo, RedisTlsParams, Result, SslOptions,
|
||||||
};
|
};
|
||||||
use crate::{RedisConfig, RedisConnectionAddr};
|
use crate::{RedisConfig, RedisConnectionAddr};
|
||||||
|
|
@ -279,22 +279,6 @@ fn parse_db_options(parsed: &Value) -> Result<Database> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_host(host: &str) -> (&str, Option<u16>, Option<&str>) {
|
|
||||||
if host.starts_with('/') {
|
|
||||||
return ("localhost", None, Some(host));
|
|
||||||
}
|
|
||||||
let mut parts = host.split(':');
|
|
||||||
let host = parts.next().unwrap();
|
|
||||||
match parts
|
|
||||||
.next()
|
|
||||||
.map(|port_or_socket| u16::from_str(port_or_socket).map_err(|_| port_or_socket))
|
|
||||||
{
|
|
||||||
Some(Ok(port)) => (host, Some(port), None),
|
|
||||||
Some(Err(socket)) => (host, None, Some(socket)),
|
|
||||||
None => (host, None, None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RedisAddress {
|
enum RedisAddress {
|
||||||
Single(RedisConnectionAddr),
|
Single(RedisConnectionAddr),
|
||||||
Cluster(Vec<RedisConnectionAddr>),
|
Cluster(Vec<RedisConnectionAddr>),
|
||||||
|
|
@ -304,36 +288,24 @@ fn parse_redis_options(parsed: &Value) -> RedisConfig {
|
||||||
let (redis_options, address) = if parsed["redis.cluster"].is_array() {
|
let (redis_options, address) = if parsed["redis.cluster"].is_array() {
|
||||||
let redis_options = &parsed["redis.cluster"];
|
let redis_options = &parsed["redis.cluster"];
|
||||||
let seeds = redis_options["seeds"].values();
|
let seeds = redis_options["seeds"].values();
|
||||||
let addresses = seeds
|
let mut addresses = seeds
|
||||||
.filter_map(|seed| seed.as_str())
|
.filter_map(|seed| seed.as_str())
|
||||||
.map(split_host)
|
.map(|seed| {
|
||||||
.filter_map(|(host, port, _)| {
|
RedisConnectionAddr::parse(seed, None, redis_options["ssl_context"].is_array())
|
||||||
Some(RedisConnectionAddr::Tcp {
|
|
||||||
host: host.into(),
|
|
||||||
port: port?,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
addresses.sort();
|
||||||
(redis_options, RedisAddress::Cluster(addresses))
|
(redis_options, RedisAddress::Cluster(addresses))
|
||||||
} else {
|
} else {
|
||||||
let redis_options = &parsed["redis"];
|
let redis_options = &parsed["redis"];
|
||||||
let mut host = redis_options["host"].as_str().unwrap_or("127.0.0.1");
|
let host = redis_options["host"].as_str().unwrap_or("127.0.0.1");
|
||||||
let address = if host.starts_with('/') {
|
let address = RedisAddress::Single(RedisConnectionAddr::parse(
|
||||||
RedisAddress::Single(RedisConnectionAddr::Unix { path: host.into() })
|
host,
|
||||||
} else {
|
redis_options["port"]
|
||||||
if host == "localhost" {
|
.as_int()
|
||||||
host = "127.0.0.1";
|
.and_then(|port| u16::try_from(port).ok()),
|
||||||
}
|
redis_options["ssl_context"].is_array(),
|
||||||
let (host, port, _) = if let Some(port) = redis_options["port"].as_int() {
|
));
|
||||||
(host, Some(port as u16), None)
|
|
||||||
} else {
|
|
||||||
split_host(host)
|
|
||||||
};
|
|
||||||
RedisAddress::Single(RedisConnectionAddr::Tcp {
|
|
||||||
host: host.into(),
|
|
||||||
port: port.unwrap_or(6379),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
(redis_options, address)
|
(redis_options, address)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use nextcloud_config_parser::{
|
use nextcloud_config_parser::{
|
||||||
parse, parse_glob, Config, Database, DbConnect, RedisConfig, RedisConnectionAddr,
|
parse, parse_glob, Config, Database, DbConnect, RedisClusterConnectionInfo, RedisConfig,
|
||||||
RedisConnectionInfo, RedisTlsParams, SslOptions,
|
RedisConnectionAddr, RedisConnectionInfo, RedisTlsParams, SslOptions,
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
|
@ -24,9 +24,16 @@ fn config_from_file(path: &str) -> Config {
|
||||||
fn parse_redis(cfg: &str) -> RedisConnectionInfo {
|
fn parse_redis(cfg: &str) -> RedisConnectionInfo {
|
||||||
let redis = ConnectionInfo::from_str(cfg).unwrap();
|
let redis = ConnectionInfo::from_str(cfg).unwrap();
|
||||||
let addr = match redis.addr {
|
let addr = match redis.addr {
|
||||||
ConnectionAddr::Tcp(host, port) | ConnectionAddr::TcpTls { host, port, .. } => {
|
ConnectionAddr::Tcp(host, port) => RedisConnectionAddr::Tcp {
|
||||||
RedisConnectionAddr::Tcp { host, port }
|
host,
|
||||||
}
|
port,
|
||||||
|
tls: false,
|
||||||
|
},
|
||||||
|
ConnectionAddr::TcpTls { host, port, .. } => RedisConnectionAddr::Tcp {
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
tls: true,
|
||||||
|
},
|
||||||
ConnectionAddr::Unix(path) => RedisConnectionAddr::Unix { path },
|
ConnectionAddr::Unix(path) => RedisConnectionAddr::Unix { path },
|
||||||
};
|
};
|
||||||
RedisConnectionInfo {
|
RedisConnectionInfo {
|
||||||
|
|
@ -109,6 +116,7 @@ fn test_parse_redis_tls() {
|
||||||
addr: RedisConnectionAddr::Tcp {
|
addr: RedisConnectionAddr::Tcp {
|
||||||
host: "127.0.0.1".into(),
|
host: "127.0.0.1".into(),
|
||||||
port: 6379,
|
port: 6379,
|
||||||
|
tls: true,
|
||||||
},
|
},
|
||||||
db: 0,
|
db: 0,
|
||||||
username: None,
|
username: None,
|
||||||
|
|
@ -125,6 +133,38 @@ fn test_parse_redis_tls() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_redis_cluster_tls() {
|
||||||
|
let config = config_from_file("tests/configs/redis_cluster_tls.php");
|
||||||
|
assert_debug_equal(
|
||||||
|
RedisConfig::Cluster(RedisClusterConnectionInfo {
|
||||||
|
addr: vec![
|
||||||
|
RedisConnectionAddr::Tcp {
|
||||||
|
host: "db1".into(),
|
||||||
|
port: 6380,
|
||||||
|
tls: true,
|
||||||
|
},
|
||||||
|
RedisConnectionAddr::Tcp {
|
||||||
|
host: "db1".into(),
|
||||||
|
port: 6381,
|
||||||
|
tls: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
db: 0,
|
||||||
|
username: None,
|
||||||
|
password: Some("xxx".into()),
|
||||||
|
tls_params: Some(RedisTlsParams {
|
||||||
|
local_cert: Some("/certs/redis.crt".into()),
|
||||||
|
local_pk: Some("/certs/redis.key".into()),
|
||||||
|
ca_file: Some("/certs/ca.crt".into()),
|
||||||
|
insecure: false,
|
||||||
|
accept_invalid_hostname: false,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
config.redis,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_comment_whitespace() {
|
fn test_parse_comment_whitespace() {
|
||||||
let config = config_from_file("tests/configs/comment_whitespace.php");
|
let config = config_from_file("tests/configs/comment_whitespace.php");
|
||||||
|
|
|
||||||
29
tests/configs/redis_cluster_tls.php
Normal file
29
tests/configs/redis_cluster_tls.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$CONFIG = [
|
||||||
|
'overwrite.cli.url' => 'https://cloud.example.com',
|
||||||
|
'dbtype' => 'mysql',
|
||||||
|
'dbname' => 'nextcloud',
|
||||||
|
'dbhost' => '127.0.0.1',
|
||||||
|
'dbport' => '',
|
||||||
|
'dbtableprefix' => 'oc_',
|
||||||
|
'dbuser' => 'nextcloud',
|
||||||
|
'dbpassword' => 'secret',
|
||||||
|
'redis.cluster' =>
|
||||||
|
array (
|
||||||
|
'seeds' =>
|
||||||
|
array (
|
||||||
|
0 => 'db1:6380',
|
||||||
|
1 => 'db1:6381',
|
||||||
|
),
|
||||||
|
'password' => 'xxx',
|
||||||
|
'timeout' => 0.0,
|
||||||
|
'read_timeout' => 0.0,
|
||||||
|
'failover_mode' => \RedisCluster::FAILOVER_ERROR,
|
||||||
|
'ssl_context' => [
|
||||||
|
'local_cert' => '/certs/redis.crt',
|
||||||
|
'local_pk' => '/certs/redis.key',
|
||||||
|
'cafile' => '/certs/ca.crt'
|
||||||
|
]
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
@ -9,7 +9,7 @@ $CONFIG = [
|
||||||
'dbuser' => 'nextcloud',
|
'dbuser' => 'nextcloud',
|
||||||
'dbpassword' => 'secret',
|
'dbpassword' => 'secret',
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'host' => 'localhost',
|
'host' => 'tls://localhost',
|
||||||
'ssl_context' => [
|
'ssl_context' => [
|
||||||
'local_cert' => '/certs/redis.crt',
|
'local_cert' => '/certs/redis.crt',
|
||||||
'local_pk' => '/certs/redis.key',
|
'local_pk' => '/certs/redis.key',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue