From 3c0c6afb6994d23e3ef806e48c4fb208f943cc40 Mon Sep 17 00:00:00 2001 From: stop5 Date: Wed, 7 May 2025 07:40:19 +0200 Subject: [PATCH 1/2] The AccountBuilder can now skip None Values By using an Serializer that takes care of the checks and transformations it converts the data into json for the authenticates_request method --- src/account.rs | 70 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/account.rs b/src/account.rs index 2ebe423..a02fe47 100644 --- a/src/account.rs +++ b/src/account.rs @@ -5,8 +5,9 @@ use crate::jws::jws; use crate::jws::Jwk; use openssl::pkey::PKey; use openssl::pkey::Private; -use serde::Deserialize; -use serde_json::json; +use serde::ser::SerializeMap; +use serde::{Deserialize,Serialize}; +use serde_json::to_value; use std::sync::Arc; use tracing::field; use tracing::instrument; @@ -84,6 +85,40 @@ pub struct AccountBuilder { only_return_existing: Option, } +impl Serialize for AccountBuilder { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer { + let mut obj = serializer.serialize_map(None)?; + if self.contact.is_some() { + obj.serialize_entry("contact", &self.contact.clone())? + } + if self.terms_of_service_agreed.is_some() { + obj.serialize_entry("termsOfServiceAgreed", &self.terms_of_service_agreed.clone())? + } + if self.only_return_existing.is_some() { + obj.serialize_entry("onlyReturnExisting", &self.only_return_existing.clone())? + } + if let Some(eab) = self.eab_config.clone() { + let payload = serde_json::to_string(&Jwk::new(&self.private_key.clone().unwrap())).unwrap(); + let binding = match jws( + &self.directory.new_account_url.clone(), + None, + &payload, + &eab.private_key, + Some(eab.key_id.clone()), + ) { + Ok(b)=>b, + Err(error)=>{ + return Err(serde::ser::Error::custom(error.to_string())) + }, + }; + obj.serialize_entry("externalAccountBinding", &binding)? + } + obj.end() +} +} + impl AccountBuilder { /// This creates a new [`AccountBuilder`]. This can be used to create a new /// account (if the server has not seen the private key before), or to retrieve @@ -147,39 +182,18 @@ impl AccountBuilder { /// through the [`Account::private_key`] method. #[instrument(level = Level::INFO, name = "acme2::AccountBuilder::build", err, skip(self), fields(contact = ?self.contact, terms_of_service_agreed = ?self.terms_of_service_agreed, only_return_existing = ?self.only_return_existing, private_key_id = field::Empty))] pub async fn build(&mut self) -> Result, Error> { - let private_key = if let Some(private_key) = self.private_key.clone() { - private_key - } else { - gen_rsa_private_key(4096)? - }; + if self.private_key.is_none() { + self.private_key = Some(gen_rsa_private_key(4096)?); + } + let private_key = self.private_key.clone().unwrap(); let url = self.directory.new_account_url.clone(); - let external_account_binding = if let Some(eab_config) = &self.eab_config { - let payload = serde_json::to_string(&Jwk::new(&private_key)).unwrap(); - - Some(jws( - &url, - None, - &payload, - &eab_config.private_key, - Some(eab_config.key_id.clone()), - )?) - } else { - None - }; - let (res, headers) = self .directory .authenticated_request::<_, Account>( &url, - json!({ - "contact": self.contact, - "termsOfServiceAgreed": self.terms_of_service_agreed, - "onlyReturnExisting": self.only_return_existing, - // TODO: omit if None? - "externalAccountBinding": external_account_binding, - }), + to_value(&self).expect("Error Serializing the request"), private_key.clone(), None, ) From ce7477aeb4176bda76c9ab5475270c2fe010aa5c Mon Sep 17 00:00:00 2001 From: Sebastian Tobie Date: Wed, 7 May 2025 18:49:53 +0200 Subject: [PATCH 2/2] fixed an possible error due of an missing None check --- src/account.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/account.rs b/src/account.rs index a02fe47..2df501c 100644 --- a/src/account.rs +++ b/src/account.rs @@ -5,8 +5,8 @@ use crate::jws::jws; use crate::jws::Jwk; use openssl::pkey::PKey; use openssl::pkey::Private; -use serde::ser::SerializeMap; -use serde::{Deserialize,Serialize}; +use serde::ser::{self, SerializeMap}; +use serde::{Deserialize, Serialize}; use serde_json::to_value; use std::sync::Arc; use tracing::field; @@ -87,20 +87,28 @@ pub struct AccountBuilder { impl Serialize for AccountBuilder { fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer { + where + S: serde::Serializer, + { let mut obj = serializer.serialize_map(None)?; if self.contact.is_some() { obj.serialize_entry("contact", &self.contact.clone())? } if self.terms_of_service_agreed.is_some() { - obj.serialize_entry("termsOfServiceAgreed", &self.terms_of_service_agreed.clone())? + obj.serialize_entry( + "termsOfServiceAgreed", + &self.terms_of_service_agreed.clone(), + )? } if self.only_return_existing.is_some() { obj.serialize_entry("onlyReturnExisting", &self.only_return_existing.clone())? } if let Some(eab) = self.eab_config.clone() { - let payload = serde_json::to_string(&Jwk::new(&self.private_key.clone().unwrap())).unwrap(); + if self.private_key.is_none() { + return Err(ser::Error::custom("private key was not set or generated.")); + } + let payload = + serde_json::to_string(&Jwk::new(&self.private_key.clone().unwrap())).unwrap(); let binding = match jws( &self.directory.new_account_url.clone(), None, @@ -108,15 +116,13 @@ impl Serialize for AccountBuilder { &eab.private_key, Some(eab.key_id.clone()), ) { - Ok(b)=>b, - Err(error)=>{ - return Err(serde::ser::Error::custom(error.to_string())) - }, + Ok(b) => b, + Err(error) => return Err(ser::Error::custom(error.to_string())), }; obj.serialize_entry("externalAccountBinding", &binding)? } obj.end() -} + } } impl AccountBuilder {