mirror of https://github.com/breard-r/acmed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
6.5 KiB
173 lines
6.5 KiB
use acme_common::error::Error;
|
|
use serde::Deserialize;
|
|
use std::fmt;
|
|
use std::str::FromStr;
|
|
|
|
pub trait ApiError {
|
|
fn get_error(&self) -> Option<Error>;
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub enum AcmeError {
|
|
AccountDoesNotExist,
|
|
AlreadyRevoked,
|
|
BadCSR,
|
|
BadNonce,
|
|
BadPublicKey,
|
|
BadRevocationReason,
|
|
BadSignatureAlgorithm,
|
|
Caa,
|
|
Compound,
|
|
Connection,
|
|
Dns,
|
|
ExternalAccountRequired,
|
|
IncorrectResponse,
|
|
InvalidContact,
|
|
Malformed,
|
|
OrderNotReady,
|
|
RateLimited,
|
|
RejectedIdentifier,
|
|
ServerInternal,
|
|
Tls,
|
|
Unauthorized,
|
|
UnsupportedContact,
|
|
UnsupportedIdentifier,
|
|
UserActionRequired,
|
|
Unknown,
|
|
}
|
|
|
|
impl From<String> for AcmeError {
|
|
fn from(error: String) -> Self {
|
|
match error.as_str() {
|
|
"urn:ietf:params:acme:error:accountDoesNotExist" => AcmeError::AccountDoesNotExist,
|
|
"urn:ietf:params:acme:error:alreadyRevoked" => AcmeError::AlreadyRevoked,
|
|
"urn:ietf:params:acme:error:badCSR" => AcmeError::BadCSR,
|
|
"urn:ietf:params:acme:error:badNonce" => AcmeError::BadNonce,
|
|
"urn:ietf:params:acme:error:badPublicKey" => AcmeError::BadPublicKey,
|
|
"urn:ietf:params:acme:error:badRevocationReason" => AcmeError::BadRevocationReason,
|
|
"urn:ietf:params:acme:error:badSignatureAlgorithm" => AcmeError::BadSignatureAlgorithm,
|
|
"urn:ietf:params:acme:error:caa" => AcmeError::Caa,
|
|
"urn:ietf:params:acme:error:compound" => AcmeError::Compound,
|
|
"urn:ietf:params:acme:error:connection" => AcmeError::Connection,
|
|
"urn:ietf:params:acme:error:dns" => AcmeError::Dns,
|
|
"urn:ietf:params:acme:error:externalAccountRequired" => {
|
|
AcmeError::ExternalAccountRequired
|
|
}
|
|
"urn:ietf:params:acme:error:incorrectResponse" => AcmeError::IncorrectResponse,
|
|
"urn:ietf:params:acme:error:invalidContact" => AcmeError::InvalidContact,
|
|
"urn:ietf:params:acme:error:malformed" => AcmeError::Malformed,
|
|
"urn:ietf:params:acme:error:orderNotReady" => AcmeError::OrderNotReady,
|
|
"urn:ietf:params:acme:error:rateLimited" => AcmeError::RateLimited,
|
|
"urn:ietf:params:acme:error:rejectedIdentifier" => AcmeError::RejectedIdentifier,
|
|
"urn:ietf:params:acme:error:serverInternal" => AcmeError::ServerInternal,
|
|
"urn:ietf:params:acme:error:tls" => AcmeError::Tls,
|
|
"urn:ietf:params:acme:error:unauthorized" => AcmeError::Unauthorized,
|
|
"urn:ietf:params:acme:error:unsupportedContact" => AcmeError::UnsupportedContact,
|
|
"urn:ietf:params:acme:error:unsupportedIdentifier" => AcmeError::UnsupportedIdentifier,
|
|
"urn:ietf:params:acme:error:userActionRequired" => AcmeError::UserActionRequired,
|
|
_ => AcmeError::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for AcmeError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let msg = match self {
|
|
AcmeError::AccountDoesNotExist => "the request specified an account that does not exist",
|
|
AcmeError::AlreadyRevoked => "the request specified a certificate to be revoked that has already been revoked",
|
|
AcmeError::BadCSR => "the CSR is unacceptable (e.g., due to a short key)",
|
|
AcmeError::BadNonce => "the client sent an unacceptable anti-replay nonce",
|
|
AcmeError::BadPublicKey => "the JWS was signed by a public key the server does not support",
|
|
AcmeError::BadRevocationReason => "the revocation reason provided is not allowed by the server",
|
|
AcmeError::BadSignatureAlgorithm => "the JWS was signed with an algorithm the server does not support",
|
|
AcmeError::Caa => "Certification Authority Authorization (CAA) records forbid the CA from issuing a certificate",
|
|
AcmeError::Compound => "specific error conditions are indicated in the \"subproblems\" array",
|
|
AcmeError::Connection => "the server could not connect to validation target",
|
|
AcmeError::Dns => "there was a problem with a DNS query during identifier validation",
|
|
AcmeError::ExternalAccountRequired => "the request must include a value for the \"externalAccountBinding\" field",
|
|
AcmeError::IncorrectResponse => "response received didn't match the challenge's requirements",
|
|
AcmeError::InvalidContact => "a contact URL for an account was invalid",
|
|
AcmeError::Malformed => "the request message was malformed",
|
|
AcmeError::OrderNotReady => "the request attempted to finalize an order that is not ready to be finalized",
|
|
AcmeError::RateLimited => "the request exceeds a rate limit",
|
|
AcmeError::RejectedIdentifier => "the server will not issue certificates for the identifier",
|
|
AcmeError::ServerInternal => "the server experienced an internal error",
|
|
AcmeError::Tls => "the server received a TLS error during validation",
|
|
AcmeError::Unauthorized => "the client lacks sufficient authorization",
|
|
AcmeError::UnsupportedContact => "a contact URL for an account used an unsupported protocol scheme",
|
|
AcmeError::UnsupportedIdentifier => "an identifier is of an unsupported type",
|
|
AcmeError::UserActionRequired => "visit the \"instance\" URL and take actions specified there",
|
|
AcmeError::Unknown => "unknown error",
|
|
};
|
|
write!(f, "{}", msg)
|
|
}
|
|
}
|
|
|
|
impl AcmeError {
|
|
pub fn is_recoverable(&self) -> bool {
|
|
*self == AcmeError::BadNonce
|
|
|| *self == AcmeError::Connection
|
|
|| *self == AcmeError::Dns
|
|
|| *self == AcmeError::Malformed
|
|
|| *self == AcmeError::RateLimited
|
|
|| *self == AcmeError::ServerInternal
|
|
|| *self == AcmeError::Tls
|
|
}
|
|
}
|
|
|
|
impl From<Error> for AcmeError {
|
|
fn from(_error: Error) -> Self {
|
|
AcmeError::Unknown
|
|
}
|
|
}
|
|
|
|
impl From<AcmeError> for Error {
|
|
fn from(error: AcmeError) -> Self {
|
|
error.to_string().into()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
|
pub struct HttpApiError {
|
|
#[serde(rename = "type")]
|
|
error_type: Option<String>,
|
|
// title: Option<String>,
|
|
status: Option<usize>,
|
|
detail: Option<String>,
|
|
// instance: Option<String>,
|
|
// TODO: implement subproblems
|
|
}
|
|
|
|
crate::acme_proto::structs::deserialize_from_str!(HttpApiError);
|
|
|
|
impl fmt::Display for HttpApiError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let msg = self
|
|
.detail
|
|
.to_owned()
|
|
.unwrap_or_else(|| self.get_acme_type().to_string());
|
|
let msg = match self.status {
|
|
Some(s) => format!("status {}: {}", s, msg),
|
|
None => msg,
|
|
};
|
|
write!(f, "{}", msg)
|
|
}
|
|
}
|
|
|
|
impl HttpApiError {
|
|
pub fn get_type(&self) -> String {
|
|
self.error_type
|
|
.to_owned()
|
|
.unwrap_or_else(|| String::from("about:blank"))
|
|
}
|
|
|
|
pub fn get_acme_type(&self) -> AcmeError {
|
|
self.get_type().into()
|
|
}
|
|
}
|
|
|
|
impl From<HttpApiError> for Error {
|
|
fn from(error: HttpApiError) -> Self {
|
|
error.to_string().into()
|
|
}
|
|
}
|