Skip to content

Add support for ACME profiles draft RFC #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ ecdsa = { version = "0.16", features = ["pem"] }
rsa = { version = "0.9", features = ["sha2"] }

[features]
default = ["acme-profiles"]
pebble = ["lazy_static", "http", "fd-lock"]
trace-requests = []
acme-profiles = []

[[example]]
name = "pebble"
Expand Down
17 changes: 15 additions & 2 deletions src/schema/directory.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Directory provides all of the URLs required to configure an ACME client for use with a specific
//! provider. It can be fetched as JSON from an advertised directory URL.

#[cfg(feature = "acme-profiles")]
use std::collections::BTreeMap;

use crate::protocol::Url;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -46,6 +49,11 @@ pub struct Metadata {
#[serde(default)]
pub website: Option<Url>,

#[cfg(feature = "acme-profiles")]
/// Supported acme profiels and their associated documentation links
#[serde(default)]
pub profiles: Option<BTreeMap<String, Url>>,

/// The hostnames that the ACME server recognizes as referring to itself for the purposes of
/// CAA record validation as defined in [RFC6844](https://www.rfc-editor.org/rfc/rfc6844).
/// Each string represents the same sequence of ASCII code points that the server
Expand Down Expand Up @@ -75,9 +83,14 @@ mod tests {
directory.new_account,
"https://example.com/acme/new-account".parse().unwrap()
);

let metadata = directory.meta.unwrap();
assert_eq!(
directory.meta.unwrap().website,
metadata.website,
Some("https://www.example.com/".parse().unwrap())
)
);

#[cfg(feature = "acme-profiles")]
assert!(metadata.profiles.unwrap().contains_key("tls-server"));
}
}
12 changes: 12 additions & 0 deletions src/schema/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub struct Order {
status: OrderStatus,
expires: Option<DateTime<Utc>>,
identifiers: Vec<Identifier>,
#[cfg(feature = "acme-profiles")]
profile: Option<String>,
not_before: Option<DateTime<Utc>>,
not_after: Option<DateTime<Utc>>,
error: Option<AcmeErrorDocument>,
Expand All @@ -64,6 +66,12 @@ impl Order {
self.identifiers.as_ref()
}

#[cfg(feature = "acme-profiles")]
/// Name of the selected certificate profile in use.
pub fn profile(&self) -> Option<&str> {
self.profile.as_deref()
}

/// The configured start time for the certificate.
pub fn not_before(&self) -> Option<DateTime<Utc>> {
self.not_before
Expand Down Expand Up @@ -123,6 +131,10 @@ pub struct NewOrderRequest {
/// A list of identifiers to include in the order.
pub identifiers: Vec<Identifier>,

#[cfg(feature = "acme-profiles")]
/// Name of the certificate profile to use.
pub profile: Option<String>,

/// Sets a time before which the issued certificate will not be valid.
pub not_before: Option<DateTime<Utc>>,

Expand Down
15 changes: 15 additions & 0 deletions src/service/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ impl<'a, K> Order<'a, K> {
pub struct OrderBuilder<'a, K> {
account: &'a Account<K>,
identifiers: Vec<Identifier>,
#[cfg(feature = "acme-profiles")]
profile: Option<String>,
not_before: Option<DateTime<Utc>>,
not_after: Option<DateTime<Utc>>,
}
Expand All @@ -251,6 +253,8 @@ impl<'a, K> OrderBuilder<'a, K> {
Self {
account,
identifiers: Vec::new(),
#[cfg(feature = "acme-profiles")]
profile: None,
not_before: None,
not_after: None,
}
Expand All @@ -272,6 +276,15 @@ impl<'a, K> OrderBuilder<'a, K> {
self
}

#[cfg(feature = "acme-profiles")]
/// Set the certificate profile to request
///
/// Certificate profiles can be found in the provider directory
pub fn profile<S: Into<String>>(mut self, profile: S) -> Self {
self.profile = Some(profile.into());
self
}

/// Set the start time for the certificate.
///
/// This certificate will be considered invalid before this timestamp.
Expand All @@ -296,6 +309,8 @@ impl<'a, K> OrderBuilder<'a, K> {
let account = self.account;
let payload = NewOrderRequest {
identifiers: self.identifiers,
#[cfg(feature = "acme-profiles")]
profile: self.profile,
not_before: self.not_before,
not_after: self.not_after,
};
Expand Down
6 changes: 5 additions & 1 deletion tests/fixtures/directory.http
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Content-Type: application/json
"termsOfService": "https://example.com/acme/terms/2017-5-30",
"website": "https://www.example.com/",
"caaIdentities": ["example.com"],
"externalAccountRequired": false
"externalAccountRequired": false,
"profiles": {
"tls-server": "https://example.com/acme/profiles/tls-server",
"email": "https://example.com/acme/profiles/email"
}
}
}
41 changes: 21 additions & 20 deletions tests/fixtures/order.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
{
"status": "valid",
"expires": "2016-01-20T14:09:07.99Z",
"identifiers": [
{
"type": "dns",
"value": "www.example.org"
},
{
"type": "dns",
"value": "example.org"
}
],
"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",
"authorizations": [
"https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI"
],
"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize",
"certificate": "https://example.com/acme/cert/mAt3xBGaobw"
"status": "valid",
"expires": "2016-01-20T14:09:07.99Z",
"identifiers": [
{
"type": "dns",
"value": "www.example.org"
},
{
"type": "dns",
"value": "example.org"
}
],
"profile": "tls-server",
"notBefore": "2016-01-01T00:00:00Z",
"notAfter": "2016-01-08T00:00:00Z",
"authorizations": [
"https://example.com/acme/authz/PAniVnsZcis",
"https://example.com/acme/authz/r4HqLzrSrpI"
],
"finalize": "https://example.com/acme/order/TOlocE8rfgo/finalize",
"certificate": "https://example.com/acme/cert/mAt3xBGaobw"
}