mirror of
https://git.proxmox.com/git/proxmox
synced 2025-07-21 21:15:44 +00:00
add more of the ACME workflow to 'Account'
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
02ecbb499c
commit
afc59f6d15
@ -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<Request, Error> {
|
||||
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<String, Error> {
|
||||
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<T: Serialize>(
|
||||
&self,
|
||||
nonce: &str,
|
||||
data: &T,
|
||||
) -> Result<Request, Error> {
|
||||
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<T: Serialize>(&self, nonce: &str) -> Result<Request, Error> {
|
||||
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<Option<GetAuthorization>, 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<Option<Request>, 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)]
|
||||
|
@ -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<String, Value>,
|
||||
}
|
||||
@ -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<Request>,
|
||||
}
|
||||
|
||||
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<Authorization, Error> {
|
||||
Ok(serde_json::from_slice(response_body)?)
|
||||
}
|
||||
}
|
||||
|
12
src/order.rs
12
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`].
|
||||
|
Loading…
Reference in New Issue
Block a user