acme/api/authorization.rs
1use serde::{Deserialize, Serialize};
2
3use crate::api;
4
5/// The status of an [`api::Order`].
6///
7/// See [RFC 8555 §7.1.4].
8///
9/// [RFC 8555 §7.1.4]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "lowercase")]
12pub enum AuthorizationStatus {
13 Pending,
14 Valid,
15 Invalid,
16 Deactivated,
17 Expired,
18 Revoked,
19}
20
21// {
22// "identifier": {
23// "type": "dns",
24// "value": "acmetest.algesten.se"
25// },
26// "status": "pending",
27// "expires": "2019-01-09T08:26:43Z",
28// "challenges": [
29// {
30// "type": "http-01",
31// "status": "pending",
32// "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789597",
33// "token": "MUi-gqeOJdRkSb_YR2eaMxQBqf6al8dgt_dOttSWb0w"
34// },
35// {
36// "type": "tls-alpn-01",
37// "status": "pending",
38// "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789598",
39// "token": "WCdRWkCy4THTD_j5IH4ISAzr59lFIg5wzYmKxuOJ1lU"
40// },
41// {
42// "type": "dns-01",
43// "status": "pending",
44// "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789599",
45// "token": "RRo2ZcXAEqxKvMH8RGcATjSK1KknLEUmauwfQ5i3gG8"
46// }
47// ]
48// }
49//
50// on incorrect challenge, something like:
51//
52// "challenges": [
53// {
54// "type": "dns-01",
55// "status": "invalid",
56// "error": {
57// "type": "urn:ietf:params:acme:error:dns",
58// "detail": "DNS problem: NXDOMAIN looking up TXT for _acme-challenge.martintest.foobar.com",
59// "status": 400
60// },
61// "url": "https://example.com/acme/challenge/afyChhlFB8GLLmIqEnqqcXzX0Ss3GBw6oUlKAGDG6lY/221695600",
62// "token": "YsNqBWZnyYjDun3aUC2CkCopOaqZRrI5hp3tUjxPLQU"
63// },
64// "Incorrect TXT record \"caOh44dp9eqXNRkd0sYrKVF8dBl0L8h8-kFpIBje-2c\" found at _acme-challenge.martintest.foobar.com
65/// An ACME authorization object.
66///
67/// Represents a server's authorization for an account to represent an identifier.
68///
69/// See [RFC 8555 §7.1.4].
70///
71/// [RFC 8555 §7.1.4]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
73pub struct Authorization {
74 /// Authorization identifier.
75 pub identifier: api::Identifier,
76
77 /// Authorization status.
78 pub status: AuthorizationStatus,
79
80 /// The timestamp after which the server will consider this authorization invalid.
81 ///
82 /// Uses RFC 3339 format.
83 ///
84 /// This field is required for objects with "valid" in the "status" field.
85 pub expires: Option<String>,
86
87 /// Returns the challenges related to the identifier.
88 ///
89 /// - For pending authorizations, the challenges that the client can fulfill in order to prove
90 /// possession of the identifier.
91 /// - For valid authorizations, the challenge that was validated.
92 /// - For invalid authorizations, the challenge that was attempted and failed.
93 ///
94 /// Each array entry is an object with parameters required to validate the challenge. A client
95 /// should attempt to fulfill one of these challenges, and a server should consider any one of
96 /// the challenges sufficient to make the authorization valid.
97 pub challenges: Vec<api::Challenge>,
98
99 /// This field MUST be present and true for authorizations created as a result of a newOrder
100 /// request containing a DNS identifier with a value that was a wildcard domain name. For other
101 /// authorizations, it MUST be absent. Wildcard domain names are described in §7.1.3.
102 pub wildcard: Option<bool>,
103}
104
105impl Authorization {
106 /// Returns true if authorization was created for a wildcard domain.
107 pub fn is_wildcard(&self) -> bool {
108 self.wildcard.unwrap_or(false)
109 }
110
111 /// Returns an `http-01` challenge, if one is present.
112 pub fn http_challenge(&self) -> Option<&api::Challenge> {
113 self.challenges.iter().find(|c| c._type == "http-01")
114 }
115
116 /// Returns a `dns-01` challenge, if one is present.
117 pub fn dns_challenge(&self) -> Option<&api::Challenge> {
118 self.challenges.iter().find(|c| c._type == "dns-01")
119 }
120
121 /// Returns a `tls-alpn-01` challenge, if one is present.
122 pub fn tls_alpn_challenge(&self) -> Option<&api::Challenge> {
123 self.challenges.iter().find(|c| c._type == "tls-alpn-01")
124 }
125}