Browse Source

Display a warning when fetching order or an authorization

Orders and authorization can both contain an error which can, for
example, help an user to fix a broken hook. It is therefore very useful
to display it.
Plus, when pooling one of those objects, having an error does not mean
we should stop pooling since the error may be temporary.
pull/5/head
Rodolphe Breard 6 years ago
parent
commit
2eff6cd799
  1. 6
      CHANGELOG.md
  2. 15
      acmed/src/acme_proto.rs
  3. 7
      acmed/src/acme_proto/http.rs
  4. 2
      acmed/src/acme_proto/structs.rs
  5. 27
      acmed/src/acme_proto/structs/authorization.rs
  6. 18
      acmed/src/acme_proto/structs/error.rs
  7. 9
      acmed/src/acme_proto/structs/order.rs

6
CHANGELOG.md

@ -13,6 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- ACMEd now displays a warning when the server indicates an error in an order or an authorization.
## [0.4.0] - 2019-05-08 ## [0.4.0] - 2019-05-08
### Added ### Added

15
acmed/src/acme_proto.rs

@ -1,12 +1,12 @@
use crate::acme_proto::account::AccountManager; use crate::acme_proto::account::AccountManager;
use crate::acme_proto::jws::encode_kid; use crate::acme_proto::jws::encode_kid;
use crate::acme_proto::structs::{ use crate::acme_proto::structs::{
Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus,
ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus,
}; };
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::storage; use crate::storage;
use acme_common::error::Error; use acme_common::error::Error;
use log::info;
use log::{info, warn};
use std::fmt; use std::fmt;
mod account; mod account;
@ -89,6 +89,9 @@ pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<
let data_builder = set_data_builder!(account, new_order.as_bytes(), directory.new_order); let data_builder = set_data_builder!(account, new_order.as_bytes(), directory.new_order);
let (order, order_url, mut nonce): (Order, String, String) = let (order, order_url, mut nonce): (Order, String, String) =
http::get_obj_loc(root_certs, &directory.new_order, &data_builder, &nonce)?; http::get_obj_loc(root_certs, &directory.new_order, &data_builder, &nonce)?;
if let Some(e) = order.get_error() {
warn!("Error: {}", e);
}
// 5. Get all the required authorizations // 5. Get all the required authorizations
for auth_url in order.authorizations.iter() { for auth_url in order.authorizations.iter() {
@ -97,6 +100,9 @@ pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<
http::get_obj(root_certs, &auth_url, &data_builder, &nonce)?; http::get_obj(root_certs, &auth_url, &data_builder, &nonce)?;
nonce = new_nonce; nonce = new_nonce;
if let Some(e) = auth.get_error() {
warn!("Error: {}", e);
}
if auth.status == AuthorizationStatus::Valid { if auth.status == AuthorizationStatus::Valid {
continue; continue;
} }
@ -148,8 +154,11 @@ pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<
let (priv_key, pub_key) = certificate::get_key_pair(cert)?; let (priv_key, pub_key) = certificate::get_key_pair(cert)?;
let csr = certificate::generate_csr(cert, &priv_key, &pub_key)?; let csr = certificate::generate_csr(cert, &priv_key, &pub_key)?;
let data_builder = set_data_builder!(account, csr.as_bytes(), order.finalize); let data_builder = set_data_builder!(account, csr.as_bytes(), order.finalize);
let (_, nonce): (Order, String) =
let (order, nonce): (Order, String) =
http::get_obj(root_certs, &order.finalize, &data_builder, &nonce)?; http::get_obj(root_certs, &order.finalize, &data_builder, &nonce)?;
if let Some(e) = order.get_error() {
warn!("Error: {}", e);
}
// 12. Pool the order in order to see whether or not it is valid // 12. Pool the order in order to see whether or not it is valid
let data_builder = set_empty_data_builder!(account, order_url); let data_builder = set_empty_data_builder!(account, order_url);

7
acmed/src/acme_proto/http.rs

@ -1,4 +1,4 @@
use crate::acme_proto::structs::{AcmeError, Directory, HttpApiError};
use crate::acme_proto::structs::{AcmeError, ApiError, Directory, HttpApiError};
use acme_common::error::Error; use acme_common::error::Error;
use http_req::request::{Method, Request}; use http_req::request::{Method, Request};
use http_req::response::Response; use http_req::response::Response;
@ -213,7 +213,7 @@ pub fn pool_obj<T, G, S>(
nonce: &str, nonce: &str,
) -> Result<(T, String), Error> ) -> Result<(T, String), Error>
where where
T: std::str::FromStr<Err = Error>,
T: std::str::FromStr<Err = Error> + ApiError,
G: Fn(&str) -> Result<String, Error>, G: Fn(&str) -> Result<String, Error>,
S: Fn(&T) -> bool, S: Fn(&T) -> bool,
{ {
@ -224,6 +224,9 @@ where
if break_fn(&obj) { if break_fn(&obj) {
return Ok((obj, new_nonce)); return Ok((obj, new_nonce));
} }
if let Some(e) = obj.get_error() {
warn!("Error: {}", e);
}
nonce = new_nonce; nonce = new_nonce;
} }
let msg = format!("Pooling failed for {}", url); let msg = format!("Pooling failed for {}", url);

2
acmed/src/acme_proto/structs.rs

@ -22,5 +22,5 @@ pub use account::{Account, AccountDeactivation, AccountResponse, AccountUpdate};
pub use authorization::{Authorization, AuthorizationStatus, Challenge}; pub use authorization::{Authorization, AuthorizationStatus, Challenge};
pub use deserialize_from_str; pub use deserialize_from_str;
pub use directory::Directory; pub use directory::Directory;
pub use error::{AcmeError, HttpApiError};
pub use error::{AcmeError, ApiError, HttpApiError};
pub use order::{Identifier, IdentifierType, NewOrder, Order, OrderStatus}; pub use order::{Identifier, IdentifierType, NewOrder, Order, OrderStatus};

27
acmed/src/acme_proto/structs/authorization.rs

@ -1,5 +1,5 @@
use crate::acme_proto::jws::algorithms::SignatureAlgorithm; use crate::acme_proto::jws::algorithms::SignatureAlgorithm;
use crate::acme_proto::structs::Identifier;
use crate::acme_proto::structs::{ApiError, HttpApiError, Identifier};
use acme_common::b64_encode; use acme_common::b64_encode;
use acme_common::error::Error; use acme_common::error::Error;
use openssl::pkey::{PKey, Private}; use openssl::pkey::{PKey, Private};
@ -32,6 +32,18 @@ impl FromStr for Authorization {
} }
} }
impl ApiError for Authorization {
fn get_error(&self) -> Option<Error> {
for challenge in self.challenges.iter() {
let err = challenge.get_error();
if err.is_some() {
return err;
}
}
None
}
}
#[derive(Debug, PartialEq, Eq, Deserialize)] #[derive(Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum AuthorizationStatus { pub enum AuthorizationStatus {
@ -123,12 +135,23 @@ impl Challenge {
} }
} }
impl ApiError for Challenge {
fn get_error(&self) -> Option<Error> {
match self {
Challenge::Http01(tc) | Challenge::Dns01(tc) | Challenge::TlsAlpn01(tc) => {
tc.error.to_owned().map(Error::from)
}
Challenge::Unknown => None,
}
}
}
#[derive(PartialEq, Deserialize)] #[derive(PartialEq, Deserialize)]
pub struct TokenChallenge { pub struct TokenChallenge {
pub url: String, pub url: String,
pub status: Option<ChallengeStatus>, pub status: Option<ChallengeStatus>,
pub validated: Option<String>, pub validated: Option<String>,
pub error: Option<String>, // TODO: set the correct object
pub error: Option<HttpApiError>,
pub token: String, pub token: String,
} }

18
acmed/src/acme_proto/structs/error.rs

@ -3,6 +3,10 @@ use serde::Deserialize;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
pub trait ApiError {
fn get_error(&self) -> Option<Error>;
}
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum AcmeError { pub enum AcmeError {
AccountDoesNotExist, AccountDoesNotExist,
@ -123,12 +127,12 @@ impl From<AcmeError> for Error {
} }
} }
#[derive(Deserialize)]
#[derive(Clone, PartialEq, Deserialize)]
pub struct HttpApiError { pub struct HttpApiError {
#[serde(rename = "type")] #[serde(rename = "type")]
error_type: Option<String>, error_type: Option<String>,
// title: Option<String>, // title: Option<String>,
// status: Option<usize>,
status: Option<usize>,
detail: Option<String>, detail: Option<String>,
// instance: Option<String>, // instance: Option<String>,
// TODO: implement subproblems // TODO: implement subproblems
@ -142,6 +146,10 @@ impl fmt::Display for HttpApiError {
.detail .detail
.to_owned() .to_owned()
.unwrap_or_else(|| self.get_acme_type().to_string()); .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) write!(f, "{}", msg)
} }
} }
@ -157,3 +165,9 @@ impl HttpApiError {
self.get_type().into() self.get_type().into()
} }
} }
impl From<HttpApiError> for Error {
fn from(error: HttpApiError) -> Self {
error.to_string().into()
}
}

9
acmed/src/acme_proto/structs/order.rs

@ -1,3 +1,4 @@
use crate::acme_proto::structs::{ApiError, HttpApiError};
use acme_common::error::Error; use acme_common::error::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@ -28,12 +29,18 @@ pub struct Order {
pub identifiers: Vec<Identifier>, pub identifiers: Vec<Identifier>,
pub not_before: Option<String>, pub not_before: Option<String>,
pub not_after: Option<String>, pub not_after: Option<String>,
pub error: Option<String>, // TODO: set the correct structure
pub error: Option<HttpApiError>,
pub authorizations: Vec<String>, pub authorizations: Vec<String>,
pub finalize: String, pub finalize: String,
pub certificate: Option<String>, pub certificate: Option<String>,
} }
impl ApiError for Order {
fn get_error(&self) -> Option<Error> {
self.error.to_owned().map(Error::from)
}
}
deserialize_from_str!(Order); deserialize_from_str!(Order);
#[derive(Debug, PartialEq, Eq, Deserialize)] #[derive(Debug, PartialEq, Eq, Deserialize)]

Loading…
Cancel
Save