Browse Source

Use a table of table for the hooks and groups

ng
Rodolphe Bréard 4 weeks ago
parent
commit
0f7ea58667
Failed to extract signature
  1. 2
      CHANGELOG.md
  2. 18
      man/en/acmed.toml.5
  3. 41
      src/config.rs
  4. 43
      src/config/hook.rs
  5. 33
      tests/config/simple/simple.toml

2
CHANGELOG.md

@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Instead of loading a default configuration file, ACMEd now loads all the - Instead of loading a default configuration file, ACMEd now loads all the
files from a default configuration directory (by default, files from a default configuration directory (by default,
`/etc/acmed/conf-enabled`). `/etc/acmed/conf-enabled`).
- The configuration arrays for accounts, endpoints, rate limits, hooks and
groups has been replaced by tables.
### Removed ### Removed

18
man/en/acmed.toml.5

@ -18,7 +18,7 @@ are written in the
format. The allowed elements are described below. format. The allowed elements are described below.
.Bl -tag .Bl -tag
.It Ic account .It Ic account
Table of table representing an account on one or several endpoint.
Table representing an account on one or several endpoint.
.Bl -tag .Bl -tag
.It Ic contacts Ar array .It Ic contacts Ar array
Array of tables describing describing the account holder's contact information. Each table must have one and only one key-value pair. Possible keys and their associated values are: Array of tables describing describing the account holder's contact information. Each table must have one and only one key-value pair. Possible keys and their associated values are:
@ -235,7 +235,7 @@ Table where the certificate's subject attributes are specified. Possible keys, w
.El .El
.El .El
.It Ic endpoint .It Ic endpoint
Table of table where each element defines a Certificate Authority
Table where each element defines a Certificate Authority
.Pq CA .Pq CA
which may be used to request certificates. which may be used to request certificates.
.Bl -tag .Bl -tag
@ -321,15 +321,9 @@ section. Default is 30d.
Array containing the path to root certificates that should be added to the trust store. Array containing the path to root certificates that should be added to the trust store.
.El .El
.It Ic group .It Ic group
Array of table allowing to group several hooks as one. A group is considered as new hook.
.Bl -tag
.It Cm hooks Ar array
Array containing the names of the hooks that are grouped. The hooks are guaranteed to be called sequentially in the declaration order.
.It Cm name Ar string
The name the group is registered under. This name is considered as a hook name. Must be unique.
.El
Table of array allowing to group several hooks as one. A group is considered as new hook. The array contains the names of the hooks that are grouped. The hooks are guaranteed to be called sequentially in the declaration order.
.It Ic hook .It Ic hook
Array of table where each element defines a command that will be launched at a defined point. See section
Table where each element defines a command that will be launched at a defined point. See section
.Sx WRITING A HOOK .Sx WRITING A HOOK
for more details. for more details.
.Bl -tag .Bl -tag
@ -339,8 +333,6 @@ Defines if an error return value for this hook is allowed or not. If not allowed
Array of strings representing the command's arguments. Array of strings representing the command's arguments.
.It Ic cmd Ar string .It Ic cmd Ar string
The name of the command that will be launched. The name of the command that will be launched.
.It Cm name Ar string
The name the hook is registered under. Must be unique.
.It Ic stderr Ar string .It Ic stderr Ar string
Path to the file where the command's standard error output if written. Path to the file where the command's standard error output if written.
.It Ic stdin Ar string .It Ic stdin Ar string
@ -379,7 +371,7 @@ post-operation
.El .El
.El .El
.It Ic rate-limit .It Ic rate-limit
Table of table where each element defines a HTTPS rate limit.
Table where each element defines a HTTPS rate limit.
.Bl -tag .Bl -tag
.It Cm number Ar integer .It Cm number Ar integer
Number of requests authorized withing the time period. Number of requests authorized withing the time period.

41
src/config.rs

@ -32,9 +32,9 @@ pub struct AcmedConfig {
#[serde(default, rename = "rate-limit")] #[serde(default, rename = "rate-limit")]
pub(in crate::config) rate_limit: HashMap<String, RateLimit>, pub(in crate::config) rate_limit: HashMap<String, RateLimit>,
#[serde(default)] #[serde(default)]
pub(in crate::config) hook: Vec<Hook>,
pub(in crate::config) hook: HashMap<String, Hook>,
#[serde(default)] #[serde(default)]
pub(in crate::config) group: Vec<Group>,
pub(in crate::config) group: HashMap<String, Vec<String>>,
#[serde(default)] #[serde(default)]
pub(in crate::config) account: HashMap<String, Account>, pub(in crate::config) account: HashMap<String, Account>,
#[serde(default)] #[serde(default)]
@ -114,12 +114,29 @@ mod tests {
global.certificates_directory, global.certificates_directory,
PathBuf::from("/tmp/example/cert/dir") PathBuf::from("/tmp/example/cert/dir")
); );
assert!(cfg.rate_limit.is_empty());
assert!(cfg.endpoint.is_empty());
assert!(cfg.hook.is_empty());
assert!(cfg.group.is_empty());
assert_eq!(cfg.rate_limit.len(), 1);
let rl = cfg.rate_limit.get("my-ca-limit").unwrap();
assert_eq!(rl.number, 20);
assert_eq!(rl.period, Duration::from_secs(1));
assert_eq!(cfg.endpoint.len(), 1);
let ep = cfg.endpoint.get("my-ca").unwrap();
assert_eq!(ep.url, "https://acme-v02.ac1.example.org/directory");
assert_eq!(ep.rate_limits, vec!["my-ca-limit".to_string()]);
assert_eq!(ep.tos_agreed, true);
assert_eq!(cfg.hook.len(), 2);
let h1 = cfg.hook.get("hook-1").unwrap();
assert_eq!(h1.cmd, "cat");
assert!(h1.args.is_empty());
assert_eq!(h1.hook_type, vec![HookType::FilePreEdit]);
let h2 = cfg.hook.get("hook-2").unwrap();
assert_eq!(h2.cmd, "cat");
assert_eq!(h2.args, vec!["-e".to_string()]);
assert_eq!(h2.hook_type, vec![HookType::FilePreEdit]);
assert_eq!(cfg.group.len(), 1);
let g1 = cfg.group.get("super-hook").unwrap();
assert_eq!(*g1, vec!["hook-1".to_string(), "hook-2".to_string()]);
assert_eq!(cfg.account.len(), 1); assert_eq!(cfg.account.len(), 1);
let account = cfg.account.get("example").unwrap();
let account = cfg.account.get("toto").unwrap();
assert_eq!(account.contacts.len(), 1); assert_eq!(account.contacts.len(), 1);
assert!(account.env.is_empty()); assert!(account.env.is_empty());
assert!(account.external_account.is_none()); assert!(account.external_account.is_none());
@ -129,7 +146,15 @@ mod tests {
account.signature_algorithm, account.signature_algorithm,
Some(AccountSignatureAlgorithm::Hs384) Some(AccountSignatureAlgorithm::Hs384)
); );
assert!(cfg.certificate.is_empty());
assert_eq!(cfg.certificate.len(), 1);
let c = cfg.certificate.first().unwrap();
assert_eq!(c.account, "toto");
assert_eq!(c.endpoint, "my-ca");
assert_eq!(c.identifiers.len(), 1);
let i = c.identifiers.first().unwrap();
assert_eq!(i.dns, Some("example.org".to_string()));
assert_eq!(i.challenge, AcmeChallenge::Http01);
assert_eq!(c.hooks, vec!["super-hook".to_string()]);
} }
#[test] #[test]

43
src/config/hook.rs

@ -11,7 +11,6 @@ pub struct Hook {
#[serde(default)] #[serde(default)]
pub(in crate::config) args: Vec<String>, pub(in crate::config) args: Vec<String>,
pub(in crate::config) cmd: String, pub(in crate::config) cmd: String,
pub(in crate::config) name: String,
pub(in crate::config) stderr: Option<PathBuf>, pub(in crate::config) stderr: Option<PathBuf>,
pub(in crate::config) stdin: Option<PathBuf>, pub(in crate::config) stdin: Option<PathBuf>,
pub(in crate::config) stdin_str: Option<String>, pub(in crate::config) stdin_str: Option<String>,
@ -62,35 +61,11 @@ pub enum HookType {
PostOperation, PostOperation,
} }
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Group {
pub(in crate::config) hooks: Vec<String>,
pub(in crate::config) name: String,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::config::load_str; use crate::config::load_str;
#[test]
fn empty_group() {
let res = load_str::<Group>("");
assert!(res.is_err());
}
#[test]
fn valid_group() {
let cfg = r#"
name = "test"
hooks = ["h1", "H 2"]
"#;
let rl: Group = load_str(cfg).unwrap();
assert_eq!(rl.name, "test");
assert_eq!(rl.hooks, vec!["h1".to_string(), "H 2".to_string()]);
}
#[test] #[test]
fn empty_hook() { fn empty_hook() {
let res = load_str::<Hook>(""); let res = load_str::<Hook>("");
@ -100,7 +75,6 @@ hooks = ["h1", "H 2"]
#[test] #[test]
fn hook_minimal() { fn hook_minimal() {
let cfg = r#" let cfg = r#"
name = "test"
cmd = "cat" cmd = "cat"
type = ["file-pre-edit"] type = ["file-pre-edit"]
"#; "#;
@ -108,7 +82,6 @@ type = ["file-pre-edit"]
assert_eq!(h.allow_failure, false); assert_eq!(h.allow_failure, false);
assert!(h.args.is_empty()); assert!(h.args.is_empty());
assert_eq!(h.cmd, "cat"); assert_eq!(h.cmd, "cat");
assert_eq!(h.name, "test");
assert!(h.stderr.is_none()); assert!(h.stderr.is_none());
assert!(h.stdin.is_none()); assert!(h.stdin.is_none());
assert!(h.stdin_str.is_none()); assert!(h.stdin_str.is_none());
@ -119,7 +92,6 @@ type = ["file-pre-edit"]
#[test] #[test]
fn hook_full() { fn hook_full() {
let cfg = r#" let cfg = r#"
name = "test"
cmd = "cat" cmd = "cat"
args = ["-e"] args = ["-e"]
type = ["file-pre-edit"] type = ["file-pre-edit"]
@ -132,7 +104,6 @@ stderr = "/tmp/err.log"
assert_eq!(h.allow_failure, true); assert_eq!(h.allow_failure, true);
assert_eq!(h.args, vec!["-e".to_string()]); assert_eq!(h.args, vec!["-e".to_string()]);
assert_eq!(h.cmd, "cat"); assert_eq!(h.cmd, "cat");
assert_eq!(h.name, "test");
assert_eq!(h.stderr, Some(PathBuf::from("/tmp/err.log"))); assert_eq!(h.stderr, Some(PathBuf::from("/tmp/err.log")));
assert_eq!(h.stdin, Some(PathBuf::from("/tmp/in.txt"))); assert_eq!(h.stdin, Some(PathBuf::from("/tmp/in.txt")));
assert!(h.stdin_str.is_none()); assert!(h.stdin_str.is_none());
@ -143,7 +114,6 @@ stderr = "/tmp/err.log"
#[test] #[test]
fn hook_both_stdin() { fn hook_both_stdin() {
let cfg = r#" let cfg = r#"
name = "test"
cmd = "cat" cmd = "cat"
type = ["file-pre-edit"] type = ["file-pre-edit"]
stdin = "/tmp/in.txt" stdin = "/tmp/in.txt"
@ -153,20 +123,9 @@ stdin_str = "some input"
assert!(res.is_err()); assert!(res.is_err());
} }
#[test]
fn hook_missing_name() {
let cfg = r#"
cmd = "cat"
type = ["file-pre-edit"]
"#;
let res = load_str::<Hook>(cfg);
assert!(res.is_err());
}
#[test] #[test]
fn hook_missing_cmd() { fn hook_missing_cmd() {
let cfg = r#" let cfg = r#"
name = "test"
type = ["file-pre-edit"] type = ["file-pre-edit"]
"#; "#;
let res = load_str::<Hook>(cfg); let res = load_str::<Hook>(cfg);
@ -176,7 +135,6 @@ type = ["file-pre-edit"]
#[test] #[test]
fn hook_missing_type() { fn hook_missing_type() {
let cfg = r#" let cfg = r#"
name = "test"
cmd = "cat" cmd = "cat"
"#; "#;
let res = load_str::<Hook>(cfg); let res = load_str::<Hook>(cfg);
@ -186,7 +144,6 @@ cmd = "cat"
#[test] #[test]
fn hook_empty_type() { fn hook_empty_type() {
let cfg = r#" let cfg = r#"
name = "test"
cmd = "cat" cmd = "cat"
type = [] type = []
"#; "#;

33
tests/config/simple/simple.toml

@ -2,8 +2,39 @@
accounts_directory = "/tmp/example/account/dir" accounts_directory = "/tmp/example/account/dir"
certificates_directory = "/tmp/example/cert/dir/" certificates_directory = "/tmp/example/cert/dir/"
[account."example"]
[account."toto"]
contacts = [ contacts = [
{ mailto = "acme@example.org" }, { mailto = "acme@example.org" },
] ]
signature_algorithm = "HS384" signature_algorithm = "HS384"
[rate-limit."my-ca-limit"]
number = 20
period = "1s"
[endpoint."my-ca"]
url = "https://acme-v02.ac1.example.org/directory"
rate_limits = ["my-ca-limit"]
tos_agreed = true
[hook."hook-1"]
cmd = "cat"
type = ["file-pre-edit"]
[hook."hook-2"]
cmd = "cat"
args = [
"-e"
]
type = ["file-pre-edit"]
[group]
super-hook = ["hook-1", "hook-2"]
[[certificate]]
account = "toto"
endpoint = "my-ca"
identifiers = [
{ dns = "example.org", challenge = "http-01"},
]
hooks = ["super-hook"]
Loading…
Cancel
Save