acme/api/order.rs
1use serde::{Deserialize, Serialize};
2
3use crate::api;
4
5/// The status of an [`api::Authorization`].
6///
7/// See [RFC 8555 §7.1.3].
8///
9/// [RFC 8555 §7.1.3]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.3
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum OrderStatus {
13 Pending,
14 Ready,
15 Processing,
16 Valid,
17 Invalid,
18}
19
20/// An ACME order object.
21///
22/// Represents a client's request for a certificate and is used to track the progress of that order
23/// through to issuance.
24///
25/// See [RFC 8555 §7.1.3].
26///
27/// [RFC 8555 §7.1.3]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.3
28///
29/// # Example JSON
30///
31/// ```json
32/// {
33/// "status": "pending",
34/// "expires": "2019-01-09T08:26:43.570360537Z",
35/// "identifiers": [
36/// {
37/// "type": "dns",
38/// "value": "acmetest.algesten.se"
39/// }
40/// ],
41/// "authorizations": [
42/// "https://example.com/acme/authz/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs"
43/// ],
44/// "finalize": "https://example.com/acme/finalize/7738992/18234324"
45/// }
46/// ```
47#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct Order {
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub status: Option<OrderStatus>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub expires: Option<String>,
55
56 pub identifiers: Vec<api::Identifier>,
57
58 ///
59 ///
60 /// Uses RFC 3339 format.
61 pub not_before: Option<String>,
62
63 ///
64 ///
65 /// Uses RFC 3339 format.
66 pub not_after: Option<String>,
67
68 pub error: Option<api::Problem>,
69 pub authorizations: Option<Vec<String>>,
70 pub finalize: String,
71 pub certificate: Option<String>,
72}
73
74impl Order {
75 pub(crate) fn from_identifiers(identifiers: Vec<api::Identifier>) -> Self {
76 Self {
77 identifiers,
78 ..Default::default()
79 }
80 }
81
82 /// Returns all domains associated with this order.
83 pub fn domains(&self) -> Vec<&str> {
84 self.identifiers
85 .iter()
86 .map(|identifier| identifier.value.as_str())
87 .collect()
88 }
89
90 /// Let's Encrypt was observed to return domains in alternate order which may flip primary with
91 /// SAN(s).
92 ///
93 /// This overwrites self without changing the order of the domains.
94 pub(crate) fn overwrite(&mut self, mut from_api: Self) -> eyre::Result<()> {
95 // Make sure the lists are the same.
96 if from_api.identifiers.len() != self.identifiers.len()
97 || from_api
98 .identifiers
99 .iter()
100 .any(|id| !self.identifiers.contains(id))
101 {
102 return Err(eyre::eyre!(
103 "Order domain(s) mismatch: had {:?} and got {:?}",
104 self.identifiers,
105 from_api.identifiers
106 ));
107 }
108
109 // Then preserve the original order.
110 from_api.identifiers = std::mem::take(&mut self.identifiers);
111 *self = from_api;
112
113 Ok(())
114 }
115}