acme/api/
authorization.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use serde::{Deserialize, Serialize};

use crate::api;

/// The status of an [`api::Order`].
///
/// See [RFC 8555 §7.1.4].
///
/// [RFC 8555 §7.1.4]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AuthorizationStatus {
    Pending,
    Valid,
    Invalid,
    Deactivated,
    Expired,
    Revoked,
}

// {
//   "identifier": {
//     "type": "dns",
//     "value": "acmetest.algesten.se"
//   },
//   "status": "pending",
//   "expires": "2019-01-09T08:26:43Z",
//   "challenges": [
//     {
//       "type": "http-01",
//       "status": "pending",
//       "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789597",
//       "token": "MUi-gqeOJdRkSb_YR2eaMxQBqf6al8dgt_dOttSWb0w"
//     },
//     {
//       "type": "tls-alpn-01",
//       "status": "pending",
//       "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789598",
//       "token": "WCdRWkCy4THTD_j5IH4ISAzr59lFIg5wzYmKxuOJ1lU"
//     },
//     {
//       "type": "dns-01",
//       "status": "pending",
//       "url": "https://example.com/acme/challenge/YTqpYUthlVfwBncUufE8IRA2TkzZkN4eYWWLMSRqcSs/216789599",
//       "token": "RRo2ZcXAEqxKvMH8RGcATjSK1KknLEUmauwfQ5i3gG8"
//     }
//   ]
// }
//
// on incorrect challenge, something like:
//
//   "challenges": [
//     {
//       "type": "dns-01",
//       "status": "invalid",
//       "error": {
//         "type": "urn:ietf:params:acme:error:dns",
//         "detail": "DNS problem: NXDOMAIN looking up TXT for _acme-challenge.martintest.foobar.com",
//         "status": 400
//       },
//       "url": "https://example.com/acme/challenge/afyChhlFB8GLLmIqEnqqcXzX0Ss3GBw6oUlKAGDG6lY/221695600",
//       "token": "YsNqBWZnyYjDun3aUC2CkCopOaqZRrI5hp3tUjxPLQU"
//     },
// "Incorrect TXT record \"caOh44dp9eqXNRkd0sYrKVF8dBl0L8h8-kFpIBje-2c\" found at _acme-challenge.martintest.foobar.com
/// An ACME authorization object.
///
/// Represents a server's authorization for an account to represent an identifier.
///
/// See [RFC 8555 §7.1.4].
///
/// [RFC 8555 §7.1.4]: https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Authorization {
    /// Authorization identifier.
    pub identifier: api::Identifier,

    /// Authorization status.
    pub status: AuthorizationStatus,

    /// The timestamp after which the server will consider this authorization invalid.
    ///
    /// Uses RFC 3339 format.
    ///
    /// This field is required for objects with "valid" in the "status" field.
    pub expires: Option<String>,

    /// Returns the challenges related to the identifier.
    ///
    /// - For pending authorizations, the challenges that the client can fulfill in order to prove
    ///   possession of the identifier.
    /// - For valid authorizations, the challenge that was validated.
    /// - For invalid authorizations, the challenge that was attempted and failed.
    ///
    /// Each array entry is an object with parameters required to validate the challenge. A client
    /// should attempt to fulfill one of these challenges, and a server should consider any one of
    /// the challenges sufficient to make the authorization valid.
    pub challenges: Vec<api::Challenge>,

    /// This field MUST be present and true for authorizations created as a result of a newOrder
    /// request containing a DNS identifier with a value that was a wildcard domain name. For other
    /// authorizations, it MUST be absent. Wildcard domain names are described in §7.1.3.
    pub wildcard: Option<bool>,
}

impl Authorization {
    /// Returns true if authorization was created for a wildcard domain.
    pub fn is_wildcard(&self) -> bool {
        self.wildcard.unwrap_or(false)
    }

    /// Returns an `http-01` challenge, if one is present.
    pub fn http_challenge(&self) -> Option<&api::Challenge> {
        self.challenges.iter().find(|c| c._type == "http-01")
    }

    /// Returns a `dns-01` challenge, if one is present.
    pub fn dns_challenge(&self) -> Option<&api::Challenge> {
        self.challenges.iter().find(|c| c._type == "dns-01")
    }

    /// Returns a `tls-alpn-01` challenge, if one is present.
    pub fn tls_alpn_challenge(&self) -> Option<&api::Challenge> {
        self.challenges.iter().find(|c| c._type == "tls-alpn-01")
    }
}