diff --git a/src/account.rs b/src/account.rs index b6a94716..83e01ad9 100644 --- a/src/account.rs +++ b/src/account.rs @@ -5,11 +5,12 @@ use openssl::pkey::{PKey, Private}; use serde::{Deserialize, Serialize}; use serde_json::Value; +use crate::authorization::{Authorization, GetAuthorization}; use crate::b64u; use crate::directory::Directory; use crate::jws::Jws; use crate::key::PublicKey; -use crate::order::{NewOrder, OrderData}; +use crate::order::{NewOrder, Order, OrderData}; use crate::request::Request; use crate::Error; @@ -117,6 +118,31 @@ impl Account { }) } + /// Prepare a JSON POST request. + fn post_request_raw_payload( + &self, + url: &str, + nonce: &str, + payload: String, + ) -> Result { + let key = PKey::private_key_from_pem(self.private_key.as_bytes())?; + let body = serde_json::to_string(&Jws::new_full( + &key, + Some(self.location.clone()), + url.to_owned(), + nonce.to_owned(), + payload, + )?)?; + + Ok(Request { + url: url.to_owned(), + method: "POST", + content_type: crate::request::JSON_CONTENT_TYPE, + body, + expected: 200, + }) + } + /// Get the "key authorization" for a token. pub fn key_authorization(&self, token: &str) -> Result { let key = PKey::private_key_from_pem(self.private_key.as_bytes())?; @@ -131,6 +157,67 @@ impl Account { let digest = openssl::sha::sha256(key_authorization.as_bytes()); Ok(b64u::encode(&digest)) } + + /// Prepare a request to update account data. + /// + /// This is a rather low level interface. You should know what you're doing. + pub fn update_account_request( + &self, + nonce: &str, + data: &T, + ) -> Result { + self.post_request(&self.location, nonce, data) + } + + /// Prepare a request to deactivate this account. + /// + /// This is a rather low level interface. You should know what you're doing. + pub fn deactivate_account_request(&self, nonce: &str) -> Result { + self.post_request_raw_payload( + &self.location, + nonce, + r#"{"status":"deactivated"}"#.to_string(), + ) + } + + /// Prepare a request to query an Authorization for an Order. + /// + /// Returns `Ok(None)` if `auth_index` is out of out of range. You can query the number of + /// authorizations from via [`Order::authorization_len`] or by manually inspecting its + /// `.data.authorization` vector. + pub fn get_authorization( + &self, + order: &Order, + auth_index: usize, + nonce: &str, + ) -> Result, Error> { + match order.authorization(auth_index) { + None => Ok(None), + Some(url) => Ok(Some(GetAuthorization::new(self.get_request(url, nonce)?))), + } + } + + /// Prepare a request to validate a Challenge from an Authorization. + /// + /// Returns `Ok(None)` if `challenge_index` is out of out of range. You can query the number of + /// challenges from via [`Authorization::challenge_len`] or by manually inspecting its + /// `.challenges` vector. + /// + /// This returns a raw `Request` since validation takes some time and the `Authorization` + /// object has to be re-queried and its `status` inspected. + pub fn validate_challenge( + &self, + authorization: &Authorization, + challenge_index: usize, + nonce: &str, + ) -> Result, Error> { + match authorization.challenges.get(challenge_index) { + None => Ok(None), + Some(challenge) => self + .post_request_raw_payload(&challenge.url, nonce, "{}".to_string()) + .map(Some), + } + } } #[derive(Clone, Copy, Eq, PartialEq, Deserialize, Serialize)] diff --git a/src/authorization.rs b/src/authorization.rs index 051306f4..f42a4cca 100644 --- a/src/authorization.rs +++ b/src/authorization.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::order::Identifier; +use crate::request::Request; +use crate::Error; #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] @@ -32,12 +34,25 @@ pub struct Authorization { pub wildcard: bool, } +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum ChallengeStatus { + Pending, + Processing, + Valid, + Invalid, +} + #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct Challenge { #[serde(rename = "type")] pub ty: String, + pub status: ChallengeStatus, + + pub url: String, + #[serde(flatten)] pub data: HashMap, } @@ -55,3 +70,24 @@ impl Challenge { fn is_false(b: &bool) -> bool { !*b } + +/// Represents an in-flight query for an authorization. +/// +/// This is created via [`Account::get_authorization`]. +pub struct GetAuthorization { + //order: OrderData, + pub request: Option, +} + +impl GetAuthorization { + pub(crate) fn new(request: Request) -> Self { + Self { + request: Some(request), + } + } + + /// Deal with the response we got from the server. + pub fn response(self, response_body: &[u8]) -> Result { + Ok(serde_json::from_slice(response_body)?) + } +} diff --git a/src/order.rs b/src/order.rs index fd6fb865..3576b96e 100644 --- a/src/order.rs +++ b/src/order.rs @@ -100,6 +100,18 @@ pub struct Order { pub data: OrderData, } +impl Order { + /// Get an authorization URL (or `None` if the index is out of range). + pub fn authorization(&self, index: usize) -> Option<&str> { + Some(&self.data.authorizations.get(index)?) + } + + /// Get the number of authorizations in this object. + pub fn authorization_len(&self) -> usize { + self.data.authorizations.len() + } +} + /// Represents a new in-flight order creation. /// /// This is created via [`Account::new_order`].