Skip to main content

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}