Skip to main content

acme_proxy_protocol/acme/
error.rs

1//! Why an ACME operation did not happen.
2//!
3//! Mostly a [`Problem`] carried as it is; the other variants exist only for
4//! the response headers a problem document cannot hold, and each still
5//! converts to the problem a client would have seen.
6
7use acme_proxy_core::error::Problem;
8
9/// The error an [`OrderService`](super::order::OrderService) or
10/// [`AccountService`](super::account::AccountService) operation returns.
11///
12/// Most refusals are protocol answers the client is owed verbatim — the type,
13/// the status and a `detail` several test suites pin byte for byte — and those
14/// travel as the [`Problem`] they already are. `Problem` is a data type as much
15/// as a response: the documents stored in `challenges.error` and `orders.error`
16/// are its RFC 7807 JSON, and only its `IntoResponse` impl belongs to the HTTP
17/// edge. Rebuilding every ACME error type here as a variant would be a second
18/// definition of each, free to drift from the first.
19///
20/// The other two variants exist because the HTTP edge needs data a problem
21/// document cannot carry: the `Link` to the terms of service, and the
22/// `Location` of the account already holding a key. Each still maps to the
23/// problem an ACME client would have seen, through `From<Error> for Problem`.
24#[derive(Debug, thiserror::Error)]
25pub enum Error {
26    /// A refusal the client reads as it is.
27    #[error("{0}")]
28    Problem(Problem),
29    /// `newAccount` without agreeing to the configured terms (RFC 8555 §7.3.3).
30    /// Its response carries a `Link` to the terms, which a problem document
31    /// cannot.
32    #[error("the terms of service have not been agreed to")]
33    TermsNotAgreed,
34    /// `keyChange` onto a key another account holds (RFC 8555 §7.3.5). Its
35    /// response carries that account's `Location`.
36    #[error("the new key already belongs to account {holder}")]
37    KeyChangeConflict { holder: uuid::Uuid },
38}
39
40impl From<Problem> for Error {
41    fn from(problem: Problem) -> Self {
42        Self::Problem(problem)
43    }
44}
45
46impl From<Error> for Problem {
47    fn from(error: Error) -> Self {
48        match error {
49            Error::Problem(problem) => problem,
50            Error::TermsNotAgreed => Problem::user_action_required(
51                "Terms of service must be agreed to before an account can be created",
52            ),
53            Error::KeyChangeConflict { .. } => Problem::key_change_conflict(
54                "The new key is already associated with a different account",
55            ),
56        }
57    }
58}