diff --git a/src/config.rs b/src/config.rs index ece4869..382f6e1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -49,22 +49,32 @@ impl<'de> Deserialize<'de> for AcmedConfig { D: Deserializer<'de>, { let unchecked = AcmedConfig::deserialize(deserializer)?; + + // Checking hooks for key in unchecked.hook.keys() { + // Hook name must not start with `internal:` if key.starts_with(crate::INTERNAL_HOOK_PREFIX) { return Err(de::Error::custom(format!("{key}: invalid hook name"))); } } + + // Checking groups for (key, hook_lst) in &unchecked.group { + // Group name must not start with `internal:` if key.starts_with(crate::INTERNAL_HOOK_PREFIX) { return Err(de::Error::custom(format!("{key}: invalid group name"))); } + // Group must only contain valid hook names for hook_name in hook_lst { if !unchecked.hook.contains_key(hook_name) { return Err(de::Error::custom(format!("{hook_name}: hook not found"))); } } } + + // Checking accoutns for account in unchecked.account.values() { + // Account must only contain valid hook/group names for hook_name in &account.hooks { if !unchecked.hook.contains_key(hook_name) && !unchecked.group.contains_key(hook_name) @@ -73,6 +83,34 @@ impl<'de> Deserialize<'de> for AcmedConfig { } } } + + // Checking certificates + for cert in &unchecked.certificate { + // Certificate must contain a valid account name + if !unchecked.account.contains_key(&cert.account) { + return Err(de::Error::custom(format!( + "{}: account not found", + cert.account + ))); + } + // Certificate must contain a valid endpoint name + if !unchecked.endpoint.contains_key(&cert.endpoint) { + return Err(de::Error::custom(format!( + "{}: endpoint not found", + cert.endpoint + ))); + } + // Certificate must only contain valid hook/group names + for hook_name in &cert.hooks { + if !unchecked.hook.contains_key(hook_name) + && !unchecked.group.contains_key(hook_name) + { + return Err(de::Error::custom(format!("{hook_name}: hook not found"))); + } + } + } + + // All tests passed Ok(unchecked) } } @@ -307,6 +345,102 @@ contacts = [ { mailto = "acme@example.org" }, ] hooks = ["not-found"] +"#; + let res = load_str::(cfg); + assert!(res.is_err()); + } + + #[test] + fn certificate() { + let cfg = r#" +[account."toto"] +contacts = [ + { mailto = "acme@example.org" }, +] + +[hook."my-hook"] +cmd = "cat" +type = ["challenge-http-01"] + +[endpoint."my-ca"] +url = "https://acme-v02.ac1.example.org/directory" + +[[certificate]] +account = "toto" +endpoint = "my-ca" +identifiers = [ + { dns = "example.org", challenge = "http-01"}, +] +hooks = ["my-hook"] +"#; + let res = load_str::(cfg); + assert!(res.is_ok()); + } + + #[test] + fn account_404_certificate() { + let cfg = r#" +[hook."my-hook"] +cmd = "cat" +type = ["challenge-http-01"] + +[endpoint."my-ca"] +url = "https://acme-v02.ac1.example.org/directory" + +[[certificate]] +account = "toto" +endpoint = "my-ca" +identifiers = [ + { dns = "example.org", challenge = "http-01"}, +] +hooks = ["my-hook"] +"#; + let res = load_str::(cfg); + assert!(res.is_err()); + } + + #[test] + fn endpoint_404_certificate() { + let cfg = r#" +[account."toto"] +contacts = [ + { mailto = "acme@example.org" }, +] + +[hook."my-hook"] +cmd = "cat" +type = ["challenge-http-01"] + +[[certificate]] +account = "toto" +endpoint = "my-ca" +identifiers = [ + { dns = "example.org", challenge = "http-01"}, +] +hooks = ["my-hook"] +"#; + let res = load_str::(cfg); + assert!(res.is_err()); + } + + #[test] + fn hook_404_certificate() { + let cfg = r#" +[account."toto"] +contacts = [ + { mailto = "acme@example.org" }, +] + +[endpoint."my-ca"] +url = "https://acme-v02.ac1.example.org/directory" + +[[certificate]] +account = "toto" +endpoint = "my-ca" +identifiers = [ + { dns = "example.org", challenge = "http-01"}, +] +hooks = ["my-hook"] "#; let res = load_str::(cfg); assert!(res.is_err());