1use crate::account::Account;
2use crate::error::*;
3use crate::helpers::Identifier;
4use crate::helpers::*;
5use crate::jws::Jwk;
6use crate::order::Order;
7use openssl::hash::hash;
8use openssl::hash::MessageDigest;
9use serde::Deserialize;
10use serde_json::json;
11use std::sync::Arc;
12use std::time::Duration;
13use tracing::debug;
14use tracing::field;
15use tracing::instrument;
16use tracing::Level;
17use tracing::Span;
18
19#[derive(Deserialize, Debug, Eq, PartialEq)]
20#[serde(rename_all = "camelCase")]
21pub enum AuthorizationStatus {
26 Pending,
27 Valid,
28 Invalid,
29 Deactivated,
30 Expired,
31 Revoked,
32}
33
34#[derive(Deserialize, Debug)]
37#[serde(rename_all = "camelCase")]
38pub struct Authorization {
39 #[serde(skip)]
40 pub(crate) account: Option<Arc<Account>>,
41 #[serde(skip)]
42 pub(crate) url: String,
43
44 pub identifier: Identifier,
46 pub status: AuthorizationStatus,
48 pub expires: Option<String>,
51 pub challenges: Vec<Challenge>,
57 pub wildcard: Option<bool>,
60}
61
62#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
66#[serde(rename_all = "camelCase")]
67pub enum ChallengeStatus {
68 Pending,
69 Processing,
70 Valid,
71 Invalid,
72}
73
74#[derive(Deserialize, Debug, Clone)]
79#[serde(rename_all = "camelCase")]
80pub struct Challenge {
81 #[serde(skip)]
82 pub(crate) account: Option<Arc<Account>>,
83
84 pub r#type: String,
86 pub(crate) url: String,
88 pub status: ChallengeStatus,
90 pub validated: Option<String>,
92
93 pub error: Option<ServerError>,
96
97 pub token: Option<String>,
99}
100
101impl Order {
102 #[instrument(level = Level::INFO, name = "acme2::Order::authorizations", err, skip(self), fields(order = %self.url, authorization_urls = ?self.authorization_urls))]
108 pub async fn authorizations(&self) -> Result<Vec<Authorization>, Error> {
109 let account = self.account.clone().unwrap();
110 let directory = account.directory.clone().unwrap();
111
112 let mut authorizations = vec![];
113
114 for authorization_url in self.authorization_urls.clone() {
115 let (res, _) = directory
116 .authenticated_request::<_, Authorization>(
117 &authorization_url,
118 "",
119 account.private_key.clone().unwrap(),
120 Some(account.id.clone()),
121 )
122 .await?;
123
124 let res: Result<Authorization, Error> = res.into();
125
126 let mut authorization = res?;
127 authorization.account = Some(account.clone());
128 authorization.url = authorization_url;
129 for challenge in &mut authorization.challenges {
130 challenge.account = Some(account.clone())
131 }
132 authorizations.push(authorization)
133 }
134
135 Ok(authorizations)
136 }
137}
138
139impl Authorization {
140 pub fn get_challenge(&self, r#type: &str) -> Option<Challenge> {
144 for challenge in &self.challenges {
145 if challenge.r#type == r#type {
146 return Some(challenge.clone());
147 }
148 }
149 None
150 }
151
152 #[instrument(level = Level::DEBUG, name = "acme2::Authorization::poll", err, skip(self), fields(url = ?self.url, status = field::Empty))]
156 pub async fn poll(self) -> Result<Authorization, Error> {
157 let account = self.account.clone().unwrap();
158 let directory = account.directory.clone().unwrap();
159
160 let (res, _) = directory
161 .authenticated_request::<_, Authorization>(
162 &self.url,
163 json!(""),
164 account.private_key.clone().unwrap(),
165 Some(account.id.clone()),
166 )
167 .await?;
168 let res: Result<Authorization, Error> = res.into();
169 let mut authorization = res?;
170 authorization.url = self.url.clone();
171 authorization.account = Some(account.clone());
172 Span::current().record("status", &field::debug(&authorization.status));
173 Ok(authorization)
174 }
175
176 #[instrument(level = Level::INFO, name = "acme2::Authorization::wait_done", err, skip(self), fields(url = ?self.url))]
190 pub async fn wait_done(
191 self,
192 poll_interval: Duration,
193 attempts: usize,
194 ) -> Result<Authorization, Error> {
195 let mut authorization = self;
196
197 let mut i: usize = 0;
198
199 while authorization.status == AuthorizationStatus::Pending {
200 if i >= attempts {
201 return Err(Error::MaxAttemptsExceeded);
202 }
203 debug!(
204 { delay = ?poll_interval },
205 "Authorization still pending. Waiting to poll."
206 );
207 tokio::time::sleep(poll_interval).await;
208 authorization = authorization.poll().await?;
209 i += 1;
210 }
211
212 Ok(authorization)
213 }
214}
215
216impl Challenge {
217 pub fn key_authorization(&self) -> Result<Option<String>, Error> {
220 if let Some(token) = self.token.clone() {
221 let account = self.account.clone().unwrap();
222
223 let key_authorization = format!(
224 "{}.{}",
225 token,
226 b64(&hash(
227 MessageDigest::sha256(),
228 &serde_json::to_string(&Jwk::new(&account.private_key.clone().unwrap()))?
229 .into_bytes()
230 )?)
231 );
232
233 Ok(Some(key_authorization))
234 } else {
235 Ok(None)
236 }
237 }
238
239 pub fn key_authorization_encoded(&self) -> Result<Option<String>, Error> {
242 let key_authorization = self.key_authorization()?;
243
244 if let Some(key_authorization) = key_authorization {
245 Ok(Some(b64(&hash(
246 MessageDigest::sha256(),
247 &key_authorization.into_bytes(),
248 )?)))
249 } else {
250 Ok(None)
251 }
252 }
253
254 #[instrument(level = Level::INFO, name = "acme2::Challenge::validate", err, skip(self), fields(url = ?self.url, status = field::Empty))]
263 pub async fn validate(&self) -> Result<Challenge, Error> {
264 let account = self.account.clone().unwrap();
265 let directory = account.directory.clone().unwrap();
266
267 let (res, _) = directory
268 .authenticated_request::<_, Challenge>(
269 &self.url,
270 json!({}),
271 account.private_key.clone().unwrap(),
272 Some(account.id.clone()),
273 )
274 .await?;
275 let res: Result<Challenge, Error> = res.into();
276 let mut challenge = res?;
277 challenge.account = Some(account.clone());
278 Span::current().record("status", &field::debug(&challenge.status));
279
280 Ok(challenge)
281 }
282
283 #[instrument(level = Level::DEBUG, name = "acme2::Challenge::poll", err, skip(self), fields(url = ?self.url, status = field::Empty))]
287 pub async fn poll(&self) -> Result<Challenge, Error> {
288 let account = self.account.clone().unwrap();
289 let directory = account.directory.clone().unwrap();
290
291 let (res, _) = directory
292 .authenticated_request::<_, Challenge>(
293 &self.url,
294 json!(""),
295 account.private_key.clone().unwrap(),
296 Some(account.id.clone()),
297 )
298 .await?;
299 let res: Result<Challenge, Error> = res.into();
300 let mut challenge = res?;
301 challenge.account = Some(account.clone());
302 Span::current().record("status", &field::debug(&challenge.status));
303 Ok(challenge)
304 }
305
306 #[instrument(level = Level::INFO, name = "acme2::Challenge::wait_done", err, skip(self), fields(url = ?self.url))]
316 pub async fn wait_done(
317 self,
318 poll_interval: Duration,
319 attempts: usize,
320 ) -> Result<Challenge, Error> {
321 let mut challenge = self;
322
323 let mut i: usize = 0;
324
325 while challenge.status == ChallengeStatus::Pending
326 || challenge.status == ChallengeStatus::Processing
327 {
328 if i >= attempts {
329 return Err(Error::MaxAttemptsExceeded);
330 }
331 debug!(
332 { delay = ?poll_interval, status = ?challenge.status },
333 "Challenge not done. Waiting to poll."
334 );
335 tokio::time::sleep(poll_interval).await;
336 challenge = challenge.poll().await?;
337 i += 1;
338 }
339
340 Ok(challenge)
341 }
342}