public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Samuel Rufinatscha <s.rufinatscha@proxmox.com>
To: "Proxmox Backup Server development discussion"
	<pbs-devel@lists.proxmox.com>,
	"Fabian Grünbichler" <f.gruenbichler@proxmox.com>
Subject: Re: [pbs-devel] [PATCH proxmox v5 1/4] acme: reduce visibility of Request type
Date: Wed, 14 Jan 2026 16:07:45 +0100	[thread overview]
Message-ID: <8183c3d2-9c35-42e0-9444-238cec66458a@proxmox.com> (raw)
In-Reply-To: <1768306497.7n6g6z55vs.astroid@yuna.none>

On 1/13/26 2:46 PM, Fabian Grünbichler wrote:
> On January 8, 2026 12:26 pm, Samuel Rufinatscha wrote:
>> Currently, the low-level ACME Request type is publicly exposed, even
>> though users are expected to go through AcmeClient and
>> proxmox-acme-api handlers. This patch reduces visibility so that
>> the Request type and related fields/methods are crate-internal only.
> 
> it also removes a lot of public and private code entirely, not just
> changing visibility.. I think those were intentionally there to allow
> usage without the need to using either of the provided client
> implementations (which are guarded behind feature flags).
> 
> if we say the crate should only be used via either the `client` or the
> `async-client` then that's fine, but it should be made explicit and
> discussed.. right now this is sort of half-way there - e.g., the
> Account::new_order method was not made private, even though it makes no
> sense anymore with those other methods/helpers removed..
> 
> this patch also breaks a few reference in doc comments that would need
> to be dropped.
> 
> a note that this breaks the current usage of proxmox-acme in PBS would
> also be good to have here, if this is kept..
>

Makes sense.

I think the best here is to drop the visibility reductions and removals
and keep the low-level API intact (as it’s currently documented and
feature-gated).

This will keep this series focused on fixing the 204 nonce handling and
switch PBS to the factored-out client / API handlers (to be on the same
base as PDM).

>>
>> Signed-off-by: Samuel Rufinatscha <s.rufinatscha@proxmox.com>
>> ---
>>   proxmox-acme/src/account.rs       | 94 ++-----------------------------
>>   proxmox-acme/src/async_client.rs  |  2 +-
>>   proxmox-acme/src/authorization.rs | 30 ----------
>>   proxmox-acme/src/client.rs        |  6 +-
>>   proxmox-acme/src/lib.rs           |  4 --
>>   proxmox-acme/src/order.rs         |  2 +-
>>   proxmox-acme/src/request.rs       | 12 ++--
>>   7 files changed, 16 insertions(+), 134 deletions(-)
>>
>> diff --git a/proxmox-acme/src/account.rs b/proxmox-acme/src/account.rs
>> index f763c1e9..d8eb3e73 100644
>> --- a/proxmox-acme/src/account.rs
>> +++ b/proxmox-acme/src/account.rs
>> @@ -8,12 +8,11 @@ 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::{Jwk, PublicKey};
>> -use crate::order::{NewOrder, Order, OrderData};
>> +use crate::order::{NewOrder, OrderData};
>>   use crate::request::Request;
>>   use crate::types::{AccountData, AccountStatus, ExternalAccountBinding};
>>   use crate::Error;
>> @@ -92,7 +91,7 @@ impl Account {
>>       }
>>   
>>       /// Prepare a "POST-as-GET" request to fetch data. Low level helper.
>> -    pub fn get_request(&self, url: &str, nonce: &str) -> Result<Request, Error> {
>> +    pub(crate) fn get_request(&self, url: &str, nonce: &str) -> 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,
>> @@ -112,7 +111,7 @@ impl Account {
>>       }
>>   
>>       /// Prepare a JSON POST request. Low level helper.
>> -    pub fn post_request<T: Serialize>(
>> +    pub(crate) fn post_request<T: Serialize>(
>>           &self,
>>           url: &str,
>>           nonce: &str,
>> @@ -136,31 +135,6 @@ 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())?;
>> @@ -176,64 +150,6 @@ impl Account {
>>           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.
>> -    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. The challenge count is
>> -    /// available by inspecting the [`Authorization::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),
>> -        }
>> -    }
>> -
>>       /// Prepare a request to revoke a certificate.
>>       ///
>>       /// The certificate can be either PEM or DER formatted.
>> @@ -274,7 +190,7 @@ pub struct CertificateRevocation<'a> {
>>   
>>   impl CertificateRevocation<'_> {
>>       /// Create the revocation request using the specified nonce for the given directory.
>> -    pub fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
>> +    pub(crate) fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
>>           let revoke_cert = directory.data.revoke_cert.as_ref().ok_or_else(|| {
>>               Error::Custom("no 'revokeCert' URL specified by provider".to_string())
>>           })?;
>> @@ -364,7 +280,7 @@ impl AccountCreator {
>>       /// the resulting request.
>>       /// Changing the private key between using the request and passing the response to
>>       /// [`response`](AccountCreator::response()) will render the account unusable!
>> -    pub fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
>> +    pub(crate) fn request(&self, directory: &Directory, nonce: &str) -> Result<Request, Error> {
>>           let key = self.key.as_deref().ok_or(Error::MissingKey)?;
>>           let url = directory.new_account_url().ok_or_else(|| {
>>               Error::Custom("no 'newAccount' URL specified by provider".to_string())
>> diff --git a/proxmox-acme/src/async_client.rs b/proxmox-acme/src/async_client.rs
>> index dc755fb9..2ff3ba22 100644
>> --- a/proxmox-acme/src/async_client.rs
>> +++ b/proxmox-acme/src/async_client.rs
>> @@ -10,7 +10,7 @@ use proxmox_http::{client::Client, Body};
>>   
>>   use crate::account::AccountCreator;
>>   use crate::order::{Order, OrderData};
>> -use crate::Request as AcmeRequest;
>> +use crate::request::Request as AcmeRequest;
>>   use crate::{Account, Authorization, Challenge, Directory, Error, ErrorResponse};
>>   
>>   /// A non-blocking Acme client using tokio/hyper.
>> diff --git a/proxmox-acme/src/authorization.rs b/proxmox-acme/src/authorization.rs
>> index 28bc1b4b..7027381a 100644
>> --- a/proxmox-acme/src/authorization.rs
>> +++ b/proxmox-acme/src/authorization.rs
>> @@ -6,8 +6,6 @@ use serde::{Deserialize, Serialize};
>>   use serde_json::Value;
>>   
>>   use crate::order::Identifier;
>> -use crate::request::Request;
>> -use crate::Error;
>>   
>>   /// Status of an [`Authorization`].
>>   #[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)]
>> @@ -132,31 +130,3 @@ impl Challenge {
>>   fn is_false(b: &bool) -> bool {
>>       !*b
>>   }
>> -
>> -/// Represents an in-flight query for an authorization.
>> -///
>> -/// This is created via [`Account::get_authorization`](crate::Account::get_authorization()).
>> -pub struct GetAuthorization {
>> -    //order: OrderData,
>> -    /// The request to send to the ACME provider. This is wrapped in an option in order to allow
>> -    /// moving it out instead of copying the contents.
>> -    ///
>> -    /// When generated via [`Account::get_authorization`](crate::Account::get_authorization()),
>> -    /// this is guaranteed to be `Some`.
>> -    ///
>> -    /// The response should be passed to the the [`response`](GetAuthorization::response()) method.
>> -    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)?)
>> -    }
>> -}
>> diff --git a/proxmox-acme/src/client.rs b/proxmox-acme/src/client.rs
>> index 931f7245..5c812567 100644
>> --- a/proxmox-acme/src/client.rs
>> +++ b/proxmox-acme/src/client.rs
>> @@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize};
>>   use crate::b64u;
>>   use crate::error;
>>   use crate::order::OrderData;
>> -use crate::request::ErrorResponse;
>> -use crate::{Account, Authorization, Challenge, Directory, Error, Order, Request};
>> +use crate::request::{ErrorResponse, Request};
>> +use crate::{Account, Authorization, Challenge, Directory, Error, Order};
>>   
>>   macro_rules! format_err {
>>       ($($fmt:tt)*) => { Error::Client(format!($($fmt)*)) };
>> @@ -564,7 +564,7 @@ impl Client {
>>       }
>>   
>>       /// Low-level API to run an n API request. This automatically updates the current nonce!
>> -    pub fn run_request(&mut self, request: Request) -> Result<HttpResponse, Error> {
>> +    pub(crate) fn run_request(&mut self, request: Request) -> Result<HttpResponse, Error> {
>>           self.inner.run_request(request)
>>       }
>>   
>> diff --git a/proxmox-acme/src/lib.rs b/proxmox-acme/src/lib.rs
>> index df722629..6722030c 100644
>> --- a/proxmox-acme/src/lib.rs
>> +++ b/proxmox-acme/src/lib.rs
>> @@ -66,10 +66,6 @@ pub use error::Error;
>>   #[doc(inline)]
>>   pub use order::Order;
>>   
>> -#[cfg(feature = "impl")]
>> -#[doc(inline)]
>> -pub use request::Request;
>> -
>>   // we don't inline these:
>>   #[cfg(feature = "impl")]
>>   pub use order::NewOrder;
>> diff --git a/proxmox-acme/src/order.rs b/proxmox-acme/src/order.rs
>> index b6551004..432a81a4 100644
>> --- a/proxmox-acme/src/order.rs
>> +++ b/proxmox-acme/src/order.rs
>> @@ -153,7 +153,7 @@ pub struct NewOrder {
>>       //order: OrderData,
>>       /// The request to execute to place the order. When creating a [`NewOrder`] via
>>       /// [`Account::new_order`](crate::Account::new_order) this is guaranteed to be `Some`.
>> -    pub request: Option<Request>,
>> +    pub(crate) request: Option<Request>,
>>   }
>>   
>>   impl NewOrder {
>> diff --git a/proxmox-acme/src/request.rs b/proxmox-acme/src/request.rs
>> index 78a90913..dadfc5af 100644
>> --- a/proxmox-acme/src/request.rs
>> +++ b/proxmox-acme/src/request.rs
>> @@ -4,21 +4,21 @@ pub(crate) const JSON_CONTENT_TYPE: &str = "application/jose+json";
>>   pub(crate) const CREATED: u16 = 201;
>>   
>>   /// A request which should be performed on the ACME provider.
>> -pub struct Request {
>> +pub(crate) struct Request {
>>       /// The complete URL to send the request to.
>> -    pub url: String,
>> +    pub(crate) url: String,
>>   
>>       /// The HTTP method name to use.
>> -    pub method: &'static str,
>> +    pub(crate) method: &'static str,
>>   
>>       /// The `Content-Type` header to pass along.
>> -    pub content_type: &'static str,
>> +    pub(crate) content_type: &'static str,
>>   
>>       /// The body to pass along with request, or an empty string.
>> -    pub body: String,
>> +    pub(crate) body: String,
>>   
>>       /// The expected status code a compliant ACME provider will return on success.
>> -    pub expected: u16,
>> +    pub(crate) expected: u16,
>>   }
>>   
>>   /// An ACME error response contains a specially formatted type string, and can optionally
>> -- 
>> 2.47.3
>>
>>
>>
>> _______________________________________________
>> pbs-devel mailing list
>> pbs-devel@lists.proxmox.com
>> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
>>
>>
>>
> 
> 
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
> 
> 



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel

  reply	other threads:[~2026-01-14 15:07 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-08 11:26 [pbs-devel] [PATCH proxmox{, -backup} v5 0/9] fix #6939: acme: support servers returning 204 for nonce requests Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox v5 1/4] acme: reduce visibility of Request type Samuel Rufinatscha
2026-01-13 13:46   ` Fabian Grünbichler
2026-01-14 15:07     ` Samuel Rufinatscha [this message]
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox v5 2/4] acme: introduce http_status module Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-14 10:29     ` Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox v5 3/4] fix #6939: acme: support servers returning 204 for nonce requests Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox v5 4/4] acme-api: add helper to load client for an account Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-13 16:57     ` Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox-backup v5 1/5] acme: clean up ACME-related imports Samuel Rufinatscha
2026-01-13 13:45   ` [pbs-devel] applied: " Fabian Grünbichler
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox-backup v5 2/5] acme: include proxmox-acme-api dependency Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-13 16:41     ` Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox-backup v5 3/5] acme: drop local AcmeClient Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-14  8:56     ` Samuel Rufinatscha
2026-01-14  9:58       ` Fabian Grünbichler
2026-01-14 10:52         ` Samuel Rufinatscha
2026-01-14 16:41           ` Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox-backup v5 4/5] acme: change API impls to use proxmox-acme-api handlers Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-13 16:53     ` Samuel Rufinatscha
2026-01-08 11:26 ` [pbs-devel] [PATCH proxmox-backup v5 5/5] acme: certificate ordering through proxmox-acme-api Samuel Rufinatscha
2026-01-13 13:45   ` Fabian Grünbichler
2026-01-13 16:51     ` Samuel Rufinatscha
2026-01-13 13:48 ` [pbs-devel] [PATCH proxmox{, -backup} v5 0/9] fix #6939: acme: support servers returning 204 for nonce requests Fabian Grünbichler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8183c3d2-9c35-42e0-9444-238cec66458a@proxmox.com \
    --to=s.rufinatscha@proxmox.com \
    --cc=f.gruenbichler@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal