mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-13 07:19:18 +01:00
seaweed-volume: read security.toml values as TOML strings (#12582)
The line parser only trimmed quotes, so `key = "abc\n"` gave the Rust volume server the bytes `abc\n` with a literal backslash while the Go servers sign with a newline, and every JWT failed. Literal strings kept their quotes and a trailing comment stayed part of the value. Decode each value with the TOML parser; text it rejects keeps the old quote trimming. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b7d6b48921
commit
c2625ddf00
1 file changed
+43
-1
@@ -1175,7 +1175,7 @@ fn parse_security_config_with_env(path: &str, env: EnvLookup<'_>) -> SecurityCon
|
|||||||
|
|
||||||
if let Some((key, value)) = trimmed.split_once('=') {
|
if let Some((key, value)) = trimmed.split_once('=') {
|
||||||
let key = key.trim();
|
let key = key.trim();
|
||||||
let value = value.trim().trim_matches('"');
|
let value: &str = &toml_value_text(value);
|
||||||
match section {
|
match section {
|
||||||
Section::JwtSigningRead => match key {
|
Section::JwtSigningRead => match key {
|
||||||
"key" => cfg.jwt_read_signing_key = value.as_bytes().to_vec(),
|
"key" => cfg.jwt_read_signing_key = value.as_bytes().to_vec(),
|
||||||
@@ -1265,6 +1265,20 @@ fn parse_security_config_with_env(path: &str, env: EnvLookup<'_>) -> SecurityCon
|
|||||||
cfg
|
cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads the right-hand side of `key = value` the way TOML (and so Go) does:
|
||||||
|
/// escapes decoded, literal strings kept as written, a trailing comment
|
||||||
|
/// dropped. Text TOML rejects keeps the plain quote trimming.
|
||||||
|
fn toml_value_text(raw: &str) -> String {
|
||||||
|
match format!("v = {raw}").parse::<toml::Table>() {
|
||||||
|
Ok(mut table) => match table.remove("v") {
|
||||||
|
Some(toml::Value::String(text)) => text,
|
||||||
|
Some(other) => other.to_string(),
|
||||||
|
None => String::new(),
|
||||||
|
},
|
||||||
|
Err(_) => raw.trim().trim_matches('"').to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_security_config_path(path: &str, env: EnvLookup<'_>) -> Option<PathBuf> {
|
fn resolve_security_config_path(path: &str, env: EnvLookup<'_>) -> Option<PathBuf> {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
return Some(PathBuf::from(path));
|
return Some(PathBuf::from(path));
|
||||||
@@ -1868,6 +1882,34 @@ ui = true
|
|||||||
assert!(cfg.access_ui);
|
assert!(cfg.access_ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_security_config_reads_toml_strings() {
|
||||||
|
for (encoded, expected) in [
|
||||||
|
(r#""plain""#, "plain"),
|
||||||
|
(r#""key\n""#, "key\n"),
|
||||||
|
(r#""quote\"slash\\tab\t""#, "quote\"slash\\tab\t"),
|
||||||
|
(r#""key""#, "key"),
|
||||||
|
("'literal\\key'", "literal\\key"),
|
||||||
|
(r#""key" # comment"#, "key"),
|
||||||
|
(r#""YWJj==""#, "YWJj=="),
|
||||||
|
("unquoted", "unquoted"),
|
||||||
|
] {
|
||||||
|
let tmp = tempfile::NamedTempFile::new().unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
tmp.path(),
|
||||||
|
format!(
|
||||||
|
"[jwt.signing]\nkey = {encoded}\n\n[jwt.signing.read]\nkey = {encoded}\n\
|
||||||
|
expires_after_seconds = 30 # seconds\n"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let cfg = parse_security_config_with_env(tmp.path().to_str().unwrap(), &|_| None);
|
||||||
|
assert_eq!(cfg.jwt_signing_key, expected.as_bytes(), "{encoded}");
|
||||||
|
assert_eq!(cfg.jwt_read_signing_key, expected.as_bytes(), "{encoded}");
|
||||||
|
assert_eq!(cfg.jwt_read_signing_expires, 30, "{encoded}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_security_config_discovers_current_directory_default() {
|
fn test_parse_security_config_discovers_current_directory_default() {
|
||||||
let _guard = process_state_lock();
|
let _guard = process_state_lock();
|
||||||
|
|||||||
Reference in new issue
Block a user