339 lines
12 KiB
Rust
339 lines
12 KiB
Rust
//! Every reason an invalid `[honcho]` section, `[[honcho.read]]` or Honcho key is refused with at
|
|
//! start (M5c spec, sections 6 and 10): each invalid section has one problem and one exact reason.
|
|
//! Do not edit.
|
|
|
|
use memoryd::config::Config;
|
|
use memoryd::honcho::config::{HonchoConfig, is_name};
|
|
|
|
/// The smallest valid section: the six keys, no read workspace, no key.
|
|
const SECTION: &str = "[honcho]
|
|
url = \"http://rift.scylla-hammerhead.ts.net:8000\"
|
|
agent = \"boxmaker0\"
|
|
owner = \"kyle\"
|
|
trusted = \"boxmaker0-trusted\"
|
|
untrusted = \"boxmaker0-untrusted\"
|
|
fleet = \"fleet\"
|
|
";
|
|
|
|
const URL_REASON: &str =
|
|
"[honcho] url must be http:// or https://, a host and an optional port, and nothing else";
|
|
|
|
/// `SECTION` with `from` (found exactly once) replaced by `to`, then `extra`.
|
|
fn with(from: &str, to: &str, extra: &str) -> String {
|
|
assert_eq!(SECTION.matches(from).count(), 1, "{from}");
|
|
format!("{}{extra}", SECTION.replace(from, to))
|
|
}
|
|
|
|
/// The checked section of `text`, which must parse.
|
|
fn checked(text: &str) -> Result<Option<HonchoConfig>, String> {
|
|
let config = Config::parse(text).unwrap_or_else(|e| panic!("{text}\n{e}"));
|
|
config.honcho_config()
|
|
}
|
|
|
|
fn reason(text: &str) -> String {
|
|
match checked(text) {
|
|
Ok(ok) => panic!("accepted:\n{text}\n{ok:?}"),
|
|
Err(why) => why,
|
|
}
|
|
}
|
|
|
|
fn name_reason(table: &str, key: &str, name: &str) -> String {
|
|
format!("{table} {key} {name:?} is not a Honcho name: 1 to 512 of a-z, A-Z, 0-9, _ and -")
|
|
}
|
|
|
|
#[test]
|
|
fn honcho_names_at_the_edges() {
|
|
let longest = "a".repeat(512);
|
|
for name in [
|
|
"a",
|
|
"Z",
|
|
"0",
|
|
"_",
|
|
"-",
|
|
"boxmaker0-trusted",
|
|
"A_b-9",
|
|
longest.as_str(),
|
|
] {
|
|
assert!(is_name(name), "{name}");
|
|
}
|
|
let too_long = "a".repeat(513);
|
|
for name in [
|
|
"",
|
|
too_long.as_str(),
|
|
"boxmaker.0",
|
|
"box maker",
|
|
"a/b",
|
|
"a:b",
|
|
"kyle@home",
|
|
"caf\u{e9}",
|
|
"tab\t",
|
|
"new\nline",
|
|
"\u{0660}",
|
|
] {
|
|
assert!(!is_name(name), "{name:?}");
|
|
}
|
|
// The longest name is accepted in every place a name goes.
|
|
let text = with("fleet = \"fleet\"", &format!("fleet = \"{longest}\""), "");
|
|
assert_eq!(checked(&text).unwrap().unwrap().fleet, longest);
|
|
let text = with("owner = \"kyle\"", &format!("owner = \"{too_long}\""), "");
|
|
assert_eq!(reason(&text), name_reason("[honcho]", "owner", &too_long));
|
|
}
|
|
|
|
#[test]
|
|
fn urls_that_are_accepted() {
|
|
for url in [
|
|
"http://rift.scylla-hammerhead.ts.net:8000",
|
|
"https://rift.scylla-hammerhead.ts.net",
|
|
"http://127.0.0.1:1",
|
|
"http://honcho:65535",
|
|
"https://Rift-2.Example.org:443",
|
|
"http://h",
|
|
] {
|
|
let text = with(
|
|
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
|
|
&format!("url = \"{url}\""),
|
|
"",
|
|
);
|
|
assert_eq!(checked(&text).unwrap().unwrap().url, url);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn urls_that_are_refused_and_the_reason_never_quotes_them() {
|
|
for url in [
|
|
"",
|
|
"rift:8000",
|
|
"ftp://rift:8000",
|
|
"HTTP://rift:8000",
|
|
"http:/rift",
|
|
"http://",
|
|
"https://",
|
|
"http://:8000",
|
|
"http://rift:",
|
|
"http://rift:0",
|
|
"http://rift:65536",
|
|
"http://rift:+80",
|
|
"http://rift:80:90",
|
|
"http://rift:8000/",
|
|
"http://rift:8000/v3",
|
|
"http://rift/",
|
|
"http://kyle:hunter2@rift:8000",
|
|
"http://kyle@rift:8000",
|
|
"http://kyle@rift",
|
|
"http://rift?x=1",
|
|
"http://rift#top",
|
|
"http://ri ft",
|
|
" http://rift",
|
|
"http://rift_1",
|
|
"http://[::1]:8000",
|
|
] {
|
|
let text = with(
|
|
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
|
|
&format!("url = \"{url}\""),
|
|
"",
|
|
);
|
|
assert_eq!(reason(&text), URL_REASON, "{url}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn each_invalid_section_gives_its_reason() {
|
|
let read = |agent: &str, workspace: &str| {
|
|
format!("[[honcho.read]]\nagent = \"{agent}\"\nworkspace = \"{workspace}\"\n")
|
|
};
|
|
let cases: Vec<(String, String)> = vec![
|
|
(
|
|
with("agent = \"boxmaker0\"", "agent = \"boxmaker.0\"", ""),
|
|
name_reason("[honcho]", "agent", "boxmaker.0"),
|
|
),
|
|
(
|
|
with("owner = \"kyle\"", "owner = \"\"", ""),
|
|
name_reason("[honcho]", "owner", ""),
|
|
),
|
|
(
|
|
with(
|
|
"trusted = \"boxmaker0-trusted\"",
|
|
"trusted = \"boxmaker0 trusted\"",
|
|
"",
|
|
),
|
|
name_reason("[honcho]", "trusted", "boxmaker0 trusted"),
|
|
),
|
|
(
|
|
with(
|
|
"untrusted = \"boxmaker0-untrusted\"",
|
|
"untrusted = \"boxmaker0/untrusted\"",
|
|
"",
|
|
),
|
|
name_reason("[honcho]", "untrusted", "boxmaker0/untrusted"),
|
|
),
|
|
(
|
|
with("fleet = \"fleet\"", "fleet = \"fleet:all\"", ""),
|
|
name_reason("[honcho]", "fleet", "fleet:all"),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker.1", "boxmaker1-trusted")),
|
|
name_reason("[[honcho.read]]", "agent", "boxmaker.1"),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker1", "boxmaker1.trusted")),
|
|
name_reason("[[honcho.read]]", "workspace", "boxmaker1.trusted"),
|
|
),
|
|
(
|
|
with("owner = \"kyle\"", "owner = \"boxmaker0\"", ""),
|
|
"[honcho] agent and owner are both \"boxmaker0\": the agent's peer must not be the owner's"
|
|
.to_string(),
|
|
),
|
|
(
|
|
with(
|
|
"untrusted = \"boxmaker0-untrusted\"",
|
|
"untrusted = \"boxmaker0-trusted\"",
|
|
"",
|
|
),
|
|
"[honcho] workspace \"boxmaker0-trusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
|
|
.to_string(),
|
|
),
|
|
(
|
|
with("fleet = \"fleet\"", "fleet = \"boxmaker0-trusted\"", ""),
|
|
"[honcho] workspace \"boxmaker0-trusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
|
|
.to_string(),
|
|
),
|
|
(
|
|
with("fleet = \"fleet\"", "fleet = \"boxmaker0-untrusted\"", ""),
|
|
"[honcho] workspace \"boxmaker0-untrusted\" is named twice: trusted, untrusted and fleet must be three different workspaces"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker0", "boxmaker1-trusted")),
|
|
"[[honcho.read]] agent \"boxmaker0\" is this agent ([honcho] agent)".to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}{}",
|
|
read("boxmaker1", "boxmaker1-trusted"),
|
|
read("boxmaker1", "boxmaker1-other")
|
|
),
|
|
"[[honcho.read]] agent \"boxmaker1\" is listed twice".to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker1", "boxmaker0-trusted")),
|
|
"[[honcho.read]] workspace \"boxmaker0-trusted\" is one of this agent's own"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker1", "boxmaker0-untrusted")),
|
|
"[[honcho.read]] workspace \"boxmaker0-untrusted\" is one of this agent's own"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}{}", read("boxmaker1", "fleet")),
|
|
"[[honcho.read]] workspace \"fleet\" is one of this agent's own".to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}trust = \"Trusted\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[[honcho.read]] trust \"Trusted\" of agent \"boxmaker1\" is not \"trusted\" or \"untrusted\""
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}trust = \"\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[[honcho.read]] trust \"\" of agent \"boxmaker1\" is not \"trusted\" or \"untrusted\""
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}class = \"top-secret\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[[honcho.read]] class \"top-secret\" of agent \"boxmaker1\" is not \"public\", \"private\" or \"secret\""
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}class = \"Private\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[[honcho.read]] class \"Private\" of agent \"boxmaker1\" is not \"public\", \"private\" or \"secret\""
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}[secrets.honcho_trust]\nenv = \"HONCHO_KEY\"\n"),
|
|
"[secrets.honcho_trust] is not a Honcho key: memoryd has only honcho_trusted, honcho_untrusted, honcho_fleet and honcho_read_<agent>"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}[secrets.mattermost_token]\nenv = \"MM_TOKEN\"\n"),
|
|
"[secrets.mattermost_token] is not a Honcho key: memoryd has only honcho_trusted, honcho_untrusted, honcho_fleet and honcho_read_<agent>"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}[secrets.honcho_read_boxmaker2]\nenv = \"HONCHO_KEY\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[secrets.honcho_read_boxmaker2] is the key of agent \"boxmaker2\", which is not in [[honcho.read]]"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}[secrets.honcho_read_boxmaker0]\nenv = \"HONCHO_KEY\"\n"),
|
|
"[secrets.honcho_read_boxmaker0] is the key of agent \"boxmaker0\", which is not in [[honcho.read]]"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}[secrets.honcho_trusted]\n"),
|
|
"[secrets.honcho_trusted] needs exactly one of credential, env and file".to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}[secrets.honcho_fleet]\nenv = \"HONCHO_KEY\"\nfile = \"/etc/honcho.key\"\n"
|
|
),
|
|
"[secrets.honcho_fleet] needs exactly one of credential, env and file".to_string(),
|
|
),
|
|
(
|
|
format!("{SECTION}[secrets.honcho_untrusted]\nenv = \"honcho_key\"\n"),
|
|
"[secrets.honcho_untrusted] env \"honcho_key\" is not a variable name (A-Z, 0-9, _)"
|
|
.to_string(),
|
|
),
|
|
(
|
|
format!(
|
|
"{SECTION}{}[secrets.honcho_read_boxmaker1]\nfile = \"keys/boxmaker1\"\n",
|
|
read("boxmaker1", "boxmaker1-trusted")
|
|
),
|
|
"[secrets.honcho_read_boxmaker1] file \"keys/boxmaker1\" must be an absolute path"
|
|
.to_string(),
|
|
),
|
|
(
|
|
"[secrets.honcho_trusted]\nenv = \"HONCHO_KEY\"\n".to_string(),
|
|
"[secrets.honcho_trusted] is set, but there is no [honcho] section".to_string(),
|
|
),
|
|
];
|
|
for (text, want) in &cases {
|
|
assert_eq!(&reason(text), want, "\n{text}");
|
|
}
|
|
assert_eq!(cases.len(), 29);
|
|
}
|
|
|
|
#[test]
|
|
fn the_first_problem_is_the_one_reported() {
|
|
// A bad url and an owner that is the agent: the url is checked first.
|
|
let text = with(
|
|
"url = \"http://rift.scylla-hammerhead.ts.net:8000\"",
|
|
"url = \"rift\"",
|
|
"",
|
|
)
|
|
.replace("owner = \"kyle\"", "owner = \"boxmaker0\"");
|
|
assert_eq!(reason(&text), URL_REASON);
|
|
// A read agent that is this agent, with a bad trust: the agent is checked first.
|
|
let text = format!(
|
|
"{SECTION}[[honcho.read]]\nagent = \"boxmaker0\"\nworkspace = \"w\"\ntrust = \"no\"\n"
|
|
);
|
|
assert_eq!(
|
|
reason(&text),
|
|
"[[honcho.read]] agent \"boxmaker0\" is this agent ([honcho] agent)"
|
|
);
|
|
}
|