1use crate::directory::Directory;
2use crate::error::*;
3use crate::helpers::*;
4use crate::jws::jws;
5use crate::jws::Jwk;
6use openssl::pkey::PKey;
7use openssl::pkey::Private;
8use serde::Deserialize;
9use serde_json::json;
10use std::sync::Arc;
11use tracing::field;
12use tracing::instrument;
13use tracing::Level;
14use tracing::Span;
15
16#[derive(Deserialize, Eq, PartialEq, Debug, Clone)]
23#[serde(rename_all = "camelCase")]
24pub enum AccountStatus {
25 Valid,
26 Deactivated,
27 Revoked,
28}
29
30#[derive(Debug, Clone)]
31pub(crate) struct ExternalAccountBinding {
32 key_id: String,
34
35 private_key: PKey<Private>,
37}
38
39#[derive(Deserialize, Debug, Clone)]
43#[serde(rename_all = "camelCase")]
44pub struct Account {
45 #[serde(skip)]
46 pub(crate) directory: Option<Arc<Directory>>,
47
48 #[serde(skip)]
49 pub(crate) private_key: Option<PKey<Private>>,
50
51 #[serde(skip)]
52 pub(crate) eab_config: Option<ExternalAccountBinding>,
53
54 #[serde(skip)]
55 pub id: String,
57
58 pub status: AccountStatus,
60 pub contact: Option<Vec<String>>,
63 pub terms_of_service_agreed: Option<bool>,
66 }
72
73#[derive(Debug)]
76pub struct AccountBuilder {
77 directory: Arc<Directory>,
78
79 private_key: Option<PKey<Private>>,
80 eab_config: Option<ExternalAccountBinding>,
81
82 contact: Option<Vec<String>>,
83 terms_of_service_agreed: Option<bool>,
84 only_return_existing: Option<bool>,
85}
86
87impl AccountBuilder {
88 pub fn new(directory: Arc<Directory>) -> Self {
92 AccountBuilder {
93 directory,
94 private_key: None,
95 eab_config: None,
96 contact: None,
97 terms_of_service_agreed: None,
98 only_return_existing: None,
99 }
100 }
101
102 pub fn private_key(&mut self, private_key: PKey<Private>) -> &mut Self {
105 self.private_key = Some(private_key);
106 self
107 }
108
109 pub fn external_account_binding(
110 &mut self,
111 key_id: String,
112 private_key: PKey<Private>,
113 ) -> &mut Self {
114 self.eab_config = Some(ExternalAccountBinding {
115 key_id,
116 private_key,
117 });
118 self
119 }
120
121 pub fn contact(&mut self, contact: Vec<String>) -> &mut Self {
125 self.contact = Some(contact);
126 self
127 }
128
129 pub fn terms_of_service_agreed(&mut self, terms_of_service_agreed: bool) -> &mut Self {
131 self.terms_of_service_agreed = Some(terms_of_service_agreed);
132 self
133 }
134
135 pub fn only_return_existing(&mut self, only_return_existing: bool) -> &mut Self {
138 self.only_return_existing = Some(only_return_existing);
139 self
140 }
141
142 #[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))]
149 pub async fn build(&mut self) -> Result<Arc<Account>, Error> {
150 let private_key = if let Some(private_key) = self.private_key.clone() {
151 private_key
152 } else {
153 gen_rsa_private_key(4096)?
154 };
155
156 let url = self.directory.new_account_url.clone();
157
158 let external_account_binding = if let Some(eab_config) = &self.eab_config {
159 let payload = serde_json::to_string(&Jwk::new(&private_key)).unwrap();
160
161 Some(jws(
162 &url,
163 None,
164 &payload,
165 &eab_config.private_key,
166 Some(eab_config.key_id.clone()),
167 )?)
168 } else {
169 None
170 };
171
172 let (res, headers) = self
173 .directory
174 .authenticated_request::<_, Account>(
175 &url,
176 json!({
177 "contact": self.contact,
178 "termsOfServiceAgreed": self.terms_of_service_agreed,
179 "onlyReturnExisting": self.only_return_existing,
180 "externalAccountBinding": external_account_binding,
182 }),
183 private_key.clone(),
184 None,
185 )
186 .await?;
187 let res: Result<Account, Error> = res.into();
188 let mut acc = res?;
189
190 let account_id = map_transport_err(
191 headers
192 .get(reqwest::header::LOCATION)
193 .ok_or_else(|| {
194 transport_err("mandatory location header in newAccount not present")
195 })?
196 .to_str(),
197 )?
198 .to_string();
199 Span::current().record("account_id", &field::display(&account_id));
200
201 acc.directory = Some(self.directory.clone());
202 acc.private_key = Some(private_key);
203 acc.eab_config = self.eab_config.clone();
204 acc.id = account_id;
205 Ok(Arc::new(acc))
206 }
207}
208
209impl Account {
210 pub fn private_key(&self) -> PKey<Private> {
212 self.private_key.clone().unwrap()
213 }
214}