mirror of https://github.com/breard-r/acmed.git
Browse Source
Refactor the account management
Refactor the account management
Until now, the account management was archaic and it was impossible to improve it without this heavy refactoring. Accounts are now disjoint from both certificates and endpoints. They have their own hooks and their own environment variables. They are stored in a binary file instead of the PEM exports of the private and public keys. This refactoring will allow account management to evolve into something more serious, with real key rollover, contact information update and so on.pull/39/head
Rodolphe Breard
4 years ago
16 changed files with 707 additions and 165 deletions
-
11CHANGELOG.md
-
14acme_common/src/crypto/openssl_keys.rs
-
1acmed/Cargo.toml
-
250acmed/src/account.rs
-
110acmed/src/account/contact.rs
-
135acmed/src/account/storage.rs
-
38acmed/src/acme_proto.rs
-
85acmed/src/acme_proto/account.rs
-
5acmed/src/acme_proto/structs/account.rs
-
4acmed/src/acme_proto/structs/directory.rs
-
11acmed/src/certificate.rs
-
53acmed/src/config.rs
-
2acmed/src/endpoint.rs
-
77acmed/src/main_event_loop.rs
-
54acmed/src/storage.rs
-
14man/en/acmed.toml.5
@ -1,37 +1,259 @@ |
|||
use crate::acme_proto::account::register_account;
|
|||
use crate::endpoint::Endpoint;
|
|||
use crate::logs::HasLogger;
|
|||
use crate::storage::FileManager;
|
|||
use acme_common::crypto::{JwsSignatureAlgorithm, KeyType};
|
|||
use acme_common::crypto::{gen_keypair, HashFunction, JwsSignatureAlgorithm, KeyPair, KeyType};
|
|||
use acme_common::error::Error;
|
|||
use std::collections::HashMap;
|
|||
use std::fmt;
|
|||
use std::str::FromStr;
|
|||
use std::time::SystemTime;
|
|||
|
|||
mod contact;
|
|||
mod storage;
|
|||
|
|||
#[derive(Clone, Debug)]
|
|||
pub enum AccountContactType {
|
|||
Mailfrom,
|
|||
}
|
|||
|
|||
impl FromStr for AccountContactType {
|
|||
type Err = Error;
|
|||
|
|||
fn from_str(s: &str) -> Result<Self, Error> {
|
|||
match s.to_lowercase().as_str() {
|
|||
"mailfrom" => Ok(AccountContactType::Mailfrom),
|
|||
_ => Err(format!("{}: unknown contact type.", s).into()),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Display for AccountContactType {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
let s = match self {
|
|||
AccountContactType::Mailfrom => "mailfrom",
|
|||
};
|
|||
write!(f, "{}", s)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug)]
|
|||
pub struct AccountKey {
|
|||
pub creation_date: SystemTime,
|
|||
pub key: KeyPair,
|
|||
pub signature_algorithm: JwsSignatureAlgorithm,
|
|||
}
|
|||
|
|||
impl AccountKey {
|
|||
fn new(key_type: KeyType, signature_algorithm: JwsSignatureAlgorithm) -> Result<Self, Error> {
|
|||
Ok(AccountKey {
|
|||
creation_date: SystemTime::now(),
|
|||
key: gen_keypair(key_type)?,
|
|||
signature_algorithm,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug, Hash)]
|
|||
pub struct AccountEndpoint {
|
|||
pub creation_date: SystemTime,
|
|||
pub account_url: String,
|
|||
pub order_url: String,
|
|||
pub key_hash: Vec<u8>,
|
|||
pub contacts_hash: Vec<u8>,
|
|||
}
|
|||
|
|||
impl AccountEndpoint {
|
|||
pub fn new() -> Self {
|
|||
AccountEndpoint {
|
|||
creation_date: SystemTime::UNIX_EPOCH,
|
|||
account_url: String::new(),
|
|||
order_url: String::new(),
|
|||
key_hash: Vec::new(),
|
|||
contacts_hash: Vec::new(),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug)]
|
|||
pub struct Account {
|
|||
pub name: String,
|
|||
pub email: String,
|
|||
pub key_type: KeyType,
|
|||
pub signature_algorithm: JwsSignatureAlgorithm,
|
|||
pub endpoints: HashMap<String, AccountEndpoint>,
|
|||
pub contacts: Vec<contact::AccountContact>,
|
|||
pub current_key: AccountKey,
|
|||
pub past_keys: Vec<AccountKey>,
|
|||
pub file_manager: FileManager,
|
|||
}
|
|||
|
|||
impl HasLogger for Account {
|
|||
fn warn(&self, msg: &str) {
|
|||
log::warn!("account \"{}\": {}", &self.name, msg);
|
|||
}
|
|||
|
|||
fn info(&self, msg: &str) {
|
|||
log::info!("account \"{}\": {}", &self.name, msg);
|
|||
}
|
|||
|
|||
fn debug(&self, msg: &str) {
|
|||
log::debug!("account \"{}\": {}", &self.name, msg);
|
|||
}
|
|||
|
|||
fn trace(&self, msg: &str) {
|
|||
log::trace!("account \"{}\": {}", &self.name, msg);
|
|||
}
|
|||
}
|
|||
|
|||
impl Account {
|
|||
pub fn new(
|
|||
_file_manager: &FileManager,
|
|||
pub fn get_endpoint_mut(&mut self, endpoint_name: &str) -> Result<&mut AccountEndpoint, Error> {
|
|||
match self.endpoints.get_mut(endpoint_name) {
|
|||
Some(ep) => Ok(ep),
|
|||
None => {
|
|||
let msg = format!(
|
|||
"{}: unknown endpoint for account {}",
|
|||
endpoint_name, self.name
|
|||
);
|
|||
Err(msg.into())
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
pub fn get_endpoint(&self, endpoint_name: &str) -> Result<&AccountEndpoint, Error> {
|
|||
match self.endpoints.get(endpoint_name) {
|
|||
Some(ep) => Ok(ep),
|
|||
None => {
|
|||
let msg = format!(
|
|||
"{}: unknown endpoint for account {}",
|
|||
endpoint_name, self.name
|
|||
);
|
|||
Err(msg.into())
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
pub fn load(
|
|||
file_manager: &FileManager,
|
|||
name: &str,
|
|||
email: &str,
|
|||
contacts: &[(String, String)],
|
|||
key_type: &Option<String>,
|
|||
signature_algorithm: &Option<String>,
|
|||
) -> Result<Self, Error> {
|
|||
let contacts = contacts
|
|||
.iter()
|
|||
.map(|(k, v)| contact::AccountContact::new(k, v))
|
|||
.collect::<Result<Vec<contact::AccountContact>, Error>>()?;
|
|||
let key_type = match key_type {
|
|||
Some(kt) => KeyType::from_str(&kt)?,
|
|||
Some(kt) => kt.parse()?,
|
|||
None => crate::DEFAULT_ACCOUNT_KEY_TYPE,
|
|||
};
|
|||
let signature_algorithm = match signature_algorithm {
|
|||
Some(sa) => JwsSignatureAlgorithm::from_str(&sa)?,
|
|||
Some(sa) => sa.parse()?,
|
|||
None => key_type.get_default_signature_alg(),
|
|||
};
|
|||
Ok(crate::account::Account {
|
|||
key_type.check_alg_compatibility(&signature_algorithm)?;
|
|||
let account = match storage::fetch(file_manager, name)? {
|
|||
Some(mut a) => {
|
|||
a.update_keys(key_type, signature_algorithm)?;
|
|||
a.contacts = contacts;
|
|||
a
|
|||
}
|
|||
None => {
|
|||
let account = Account {
|
|||
name: name.to_string(),
|
|||
email: email.to_string(),
|
|||
key_type,
|
|||
signature_algorithm,
|
|||
})
|
|||
endpoints: HashMap::new(),
|
|||
contacts,
|
|||
current_key: AccountKey::new(key_type, signature_algorithm)?,
|
|||
past_keys: Vec::new(),
|
|||
file_manager: file_manager.clone(),
|
|||
};
|
|||
account.debug("initializing a new account");
|
|||
account
|
|||
}
|
|||
};
|
|||
Ok(account)
|
|||
}
|
|||
|
|||
pub fn add_endpoint_name(&mut self, endpoint_name: &str) {
|
|||
self.endpoints
|
|||
.entry(endpoint_name.to_string())
|
|||
.or_insert_with(AccountEndpoint::new);
|
|||
}
|
|||
|
|||
pub fn synchronize(
|
|||
&mut self,
|
|||
endpoint: &mut Endpoint,
|
|||
root_certs: &[String],
|
|||
) -> Result<(), Error> {
|
|||
register_account(endpoint, root_certs, self)?;
|
|||
Ok(())
|
|||
}
|
|||
|
|||
pub fn save(&self) -> Result<(), Error> {
|
|||
storage::save(&self.file_manager, self)
|
|||
}
|
|||
|
|||
pub fn set_account_url(&mut self, endpoint_name: &str, account_url: &str) -> Result<(), Error> {
|
|||
let mut ep = self.get_endpoint_mut(endpoint_name)?;
|
|||
ep.account_url = account_url.to_string();
|
|||
Ok(())
|
|||
}
|
|||
|
|||
pub fn set_order_url(&mut self, endpoint_name: &str, order_url: &str) -> Result<(), Error> {
|
|||
let mut ep = self.get_endpoint_mut(endpoint_name)?;
|
|||
ep.order_url = order_url.to_string();
|
|||
Ok(())
|
|||
}
|
|||
|
|||
pub fn update_key_hash(&mut self, endpoint_name: &str) -> Result<(), Error> {
|
|||
let key = self.current_key.clone();
|
|||
let mut ep = self.get_endpoint_mut(endpoint_name)?;
|
|||
ep.key_hash = hash_key(&key)?;
|
|||
Ok(())
|
|||
}
|
|||
|
|||
pub fn update_contacts_hash(&mut self, endpoint_name: &str) -> Result<(), Error> {
|
|||
let ct = self.contacts.clone();
|
|||
let mut ep = self.get_endpoint_mut(endpoint_name)?;
|
|||
ep.contacts_hash = hash_contacts(&ct);
|
|||
Ok(())
|
|||
}
|
|||
|
|||
fn update_keys(
|
|||
&mut self,
|
|||
key_type: KeyType,
|
|||
signature_algorithm: JwsSignatureAlgorithm,
|
|||
) -> Result<(), Error> {
|
|||
if self.current_key.key.key_type != key_type
|
|||
|| self.current_key.signature_algorithm != signature_algorithm
|
|||
{
|
|||
self.debug("account key has been changed in the configuration, creating a new one...");
|
|||
self.past_keys.push(self.current_key.to_owned());
|
|||
self.current_key = AccountKey::new(key_type, signature_algorithm)?;
|
|||
self.save()?;
|
|||
let msg = format!(
|
|||
"new {} account key created, using {} as signing algorithm",
|
|||
key_type, signature_algorithm
|
|||
);
|
|||
self.info(&msg);
|
|||
} else {
|
|||
self.trace("account key is up to date");
|
|||
}
|
|||
Ok(())
|
|||
}
|
|||
}
|
|||
|
|||
fn hash_contacts(contacts: &[contact::AccountContact]) -> Vec<u8> {
|
|||
let msg = contacts
|
|||
.iter()
|
|||
.map(|v| v.to_string())
|
|||
.collect::<Vec<String>>()
|
|||
.join("")
|
|||
.into_bytes();
|
|||
HashFunction::Sha256.hash(&msg)
|
|||
}
|
|||
|
|||
fn hash_key(key: &AccountKey) -> Result<Vec<u8>, Error> {
|
|||
let mut msg = key.signature_algorithm.to_string().into_bytes();
|
|||
let pem = key.key.public_key_to_pem()?;
|
|||
msg.extend_from_slice(&pem);
|
|||
Ok(HashFunction::Sha256.hash(&msg))
|
|||
}
|
@ -0,0 +1,110 @@ |
|||
use acme_common::error::Error;
|
|||
use std::fmt;
|
|||
use std::str::FromStr;
|
|||
|
|||
fn clean_mailto(value: &str) -> Result<String, Error> {
|
|||
// TODO: implement a simple RFC 6068 parser
|
|||
// - no "hfields"
|
|||
// - max one "addr-spec" in the "to" component
|
|||
Ok(value.to_string())
|
|||
}
|
|||
|
|||
// TODO: implement other URI shemes
|
|||
// https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
|
|||
// https://en.wikipedia.org/wiki/List_of_URI_schemes
|
|||
// Exemples:
|
|||
// - P1: tel, sms
|
|||
// - P2: geo, maps
|
|||
// - P3: irc, irc6, ircs, xmpp
|
|||
// - P4: sip, sips
|
|||
#[derive(Clone, Debug, PartialEq)]
|
|||
pub enum ContactType {
|
|||
Mailto,
|
|||
}
|
|||
|
|||
impl ContactType {
|
|||
pub fn clean_value(&self, value: &str) -> Result<String, Error> {
|
|||
match self {
|
|||
ContactType::Mailto => clean_mailto(value),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl FromStr for ContactType {
|
|||
type Err = Error;
|
|||
|
|||
fn from_str(s: &str) -> Result<Self, Error> {
|
|||
match s.to_lowercase().as_str() {
|
|||
"mailto" => Ok(ContactType::Mailto),
|
|||
_ => Err(format!("{}: unknown contact type.", s).into()),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Display for ContactType {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
let s = match self {
|
|||
ContactType::Mailto => "mailto",
|
|||
};
|
|||
write!(f, "{}", s)
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Clone, Debug, PartialEq)]
|
|||
pub struct AccountContact {
|
|||
pub contact_type: ContactType,
|
|||
pub value: String,
|
|||
}
|
|||
|
|||
impl AccountContact {
|
|||
pub fn new(contact_type: &str, value: &str) -> Result<Self, Error> {
|
|||
let contact_type: ContactType = contact_type.parse()?;
|
|||
let value = contact_type.clean_value(value)?;
|
|||
Ok(AccountContact {
|
|||
contact_type,
|
|||
value,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
impl fmt::Display for AccountContact {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|||
write!(f, "{}:{}", self.contact_type, self.value)
|
|||
}
|
|||
}
|
|||
|
|||
#[cfg(test)]
|
|||
mod tests {
|
|||
use super::*;
|
|||
|
|||
#[test]
|
|||
fn test_account_contact_eq() {
|
|||
let c1 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap();
|
|||
let c2 = AccountContact::new("mailto", "derp.derpson@example.com").unwrap();
|
|||
let c3 = AccountContact::new("mailto", "derp@example.com").unwrap();
|
|||
assert_eq!(c1, c2);
|
|||
assert_eq!(c2, c1);
|
|||
assert_ne!(c1, c3);
|
|||
assert_ne!(c2, c3);
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_account_contact_in_vec() {
|
|||
let contacts = vec![
|
|||
AccountContact::new("mailto", "derp.derpson@example.com").unwrap(),
|
|||
AccountContact::new("mailto", "derp@example.com").unwrap(),
|
|||
];
|
|||
let c = AccountContact::new("mailto", "derp@example.com").unwrap();
|
|||
assert!(contacts.contains(&c));
|
|||
}
|
|||
|
|||
#[test]
|
|||
fn test_account_contact_not_in_vec() {
|
|||
let contacts = vec![
|
|||
AccountContact::new("mailto", "derp.derpson@example.com").unwrap(),
|
|||
AccountContact::new("mailto", "derp@example.com").unwrap(),
|
|||
];
|
|||
let c = AccountContact::new("mailto", "derpina@example.com").unwrap();
|
|||
assert!(!contacts.contains(&c));
|
|||
}
|
|||
}
|
@ -0,0 +1,135 @@ |
|||
use crate::account::contact::AccountContact;
|
|||
use crate::account::{Account, AccountEndpoint, AccountKey};
|
|||
use crate::storage::{account_files_exists, get_account_data, set_account_data, FileManager};
|
|||
use acme_common::crypto::KeyPair;
|
|||
use acme_common::error::Error;
|
|||
use serde::{Deserialize, Serialize};
|
|||
use std::collections::HashMap;
|
|||
use std::time::SystemTime;
|
|||
|
|||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|||
struct AccountKeyStorage {
|
|||
creation_date: SystemTime,
|
|||
key: Vec<u8>,
|
|||
signature_algorithm: String,
|
|||
}
|
|||
|
|||
impl AccountKeyStorage {
|
|||
fn new(key: &AccountKey) -> Result<Self, Error> {
|
|||
Ok(AccountKeyStorage {
|
|||
creation_date: key.creation_date,
|
|||
key: key.key.private_key_to_der()?,
|
|||
signature_algorithm: key.signature_algorithm.to_string(),
|
|||
})
|
|||
}
|
|||
|
|||
fn to_generic(&self) -> Result<AccountKey, Error> {
|
|||
Ok(AccountKey {
|
|||
creation_date: self.creation_date,
|
|||
key: KeyPair::from_der(&self.key)?,
|
|||
signature_algorithm: self.signature_algorithm.parse()?,
|
|||
})
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|||
struct AccountEndpointStorage {
|
|||
creation_date: SystemTime,
|
|||
account_url: String,
|
|||
order_url: String,
|
|||
key_hash: Vec<u8>,
|
|||
contacts_hash: Vec<u8>,
|
|||
}
|
|||
|
|||
impl AccountEndpointStorage {
|
|||
fn new(account_endpoint: &AccountEndpoint) -> Self {
|
|||
AccountEndpointStorage {
|
|||
creation_date: account_endpoint.creation_date,
|
|||
account_url: account_endpoint.account_url.clone(),
|
|||
order_url: account_endpoint.order_url.clone(),
|
|||
key_hash: account_endpoint.key_hash.clone(),
|
|||
contacts_hash: account_endpoint.contacts_hash.clone(),
|
|||
}
|
|||
}
|
|||
|
|||
fn to_generic(&self) -> AccountEndpoint {
|
|||
AccountEndpoint {
|
|||
creation_date: self.creation_date,
|
|||
account_url: self.account_url.clone(),
|
|||
order_url: self.order_url.clone(),
|
|||
key_hash: self.key_hash.clone(),
|
|||
contacts_hash: self.contacts_hash.clone(),
|
|||
}
|
|||
}
|
|||
}
|
|||
|
|||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|||
struct AccountStorage {
|
|||
name: String,
|
|||
endpoints: HashMap<String, AccountEndpointStorage>,
|
|||
contacts: Vec<(String, String)>,
|
|||
current_key: AccountKeyStorage,
|
|||
past_keys: Vec<AccountKeyStorage>,
|
|||
}
|
|||
|
|||
pub fn fetch(file_manager: &FileManager, name: &str) -> Result<Option<Account>, Error> {
|
|||
if account_files_exists(file_manager) {
|
|||
let data = get_account_data(file_manager)?;
|
|||
let obj: AccountStorage = bincode::deserialize(&data[..])
|
|||
.map_err(|e| Error::from(&e.to_string()).prefix(name))?;
|
|||
let endpoints = obj
|
|||
.endpoints
|
|||
.iter()
|
|||
.map(|(k, v)| (k.clone(), v.to_generic()))
|
|||
.collect();
|
|||
let contacts = obj
|
|||
.contacts
|
|||
.iter()
|
|||
.map(|(t, v)| AccountContact::new(t, v))
|
|||
.collect::<Result<Vec<AccountContact>, Error>>()?;
|
|||
let current_key = obj.current_key.to_generic()?;
|
|||
let past_keys = obj
|
|||
.past_keys
|
|||
.iter()
|
|||
.map(|k| k.to_generic())
|
|||
.collect::<Result<Vec<AccountKey>, Error>>()?;
|
|||
Ok(Some(Account {
|
|||
name: obj.name,
|
|||
endpoints,
|
|||
contacts,
|
|||
current_key,
|
|||
past_keys,
|
|||
file_manager: file_manager.clone(),
|
|||
}))
|
|||
} else {
|
|||
Ok(None)
|
|||
}
|
|||
}
|
|||
|
|||
pub fn save(file_manager: &FileManager, account: &Account) -> Result<(), Error> {
|
|||
let endpoints: HashMap<String, AccountEndpointStorage> = account
|
|||
.endpoints
|
|||
.iter()
|
|||
.map(|(k, v)| (k.to_owned(), AccountEndpointStorage::new(v)))
|
|||
.collect();
|
|||
let contacts: Vec<(String, String)> = account
|
|||
.contacts
|
|||
.iter()
|
|||
.map(|c| (c.contact_type.to_string(), c.value.to_owned()))
|
|||
.collect();
|
|||
let past_keys = account
|
|||
.past_keys
|
|||
.iter()
|
|||
.map(|k| AccountKeyStorage::new(&k))
|
|||
.collect::<Result<Vec<AccountKeyStorage>, Error>>()?;
|
|||
let account_storage = AccountStorage {
|
|||
name: account.name.to_owned(),
|
|||
endpoints,
|
|||
contacts,
|
|||
current_key: AccountKeyStorage::new(&account.current_key)?,
|
|||
past_keys,
|
|||
};
|
|||
let encoded: Vec<u8> = bincode::serialize(&account_storage)
|
|||
.map_err(|e| Error::from(&e.to_string()).prefix(&account.name))?;
|
|||
set_account_data(file_manager, &encoded)
|
|||
}
|
@ -1,71 +1,38 @@ |
|||
use crate::account::Account as BaseAccount;
|
|||
use crate::acme_proto::http;
|
|||
use crate::acme_proto::structs::Account;
|
|||
use crate::certificate::Certificate;
|
|||
use crate::endpoint::Endpoint;
|
|||
use crate::jws::encode_jwk;
|
|||
use crate::logs::HasLogger;
|
|||
use crate::storage;
|
|||
use acme_common::crypto::{gen_keypair, JwsSignatureAlgorithm, KeyPair};
|
|||
use acme_common::error::Error;
|
|||
|
|||
pub struct AccountManager {
|
|||
pub key_pair: KeyPair,
|
|||
pub signature_algorithm: JwsSignatureAlgorithm,
|
|||
pub account_url: String,
|
|||
pub orders_url: String,
|
|||
}
|
|||
|
|||
impl AccountManager {
|
|||
pub fn new(
|
|||
pub fn register_account(
|
|||
endpoint: &mut Endpoint,
|
|||
root_certs: &[String],
|
|||
cert: &Certificate,
|
|||
) -> Result<Self, Error> {
|
|||
// TODO: store the key id (account url)
|
|||
let key_pair = storage::get_account_keypair(&cert.file_manager)?;
|
|||
let signature_algorithm = cert.account.signature_algorithm;
|
|||
let kp_ref = &key_pair;
|
|||
let account = Account::new(cert, endpoint);
|
|||
let account = serde_json::to_string(&account)?;
|
|||
let acc_ref = &account;
|
|||
let data_builder = |n: &str, url: &str| {
|
|||
encode_jwk(kp_ref, &signature_algorithm, acc_ref.as_bytes(), url, n)
|
|||
};
|
|||
let (acc_rep, account_url) = http::new_account(endpoint, root_certs, &data_builder)?;
|
|||
let ac = AccountManager {
|
|||
key_pair,
|
|||
signature_algorithm,
|
|||
account_url,
|
|||
orders_url: acc_rep.orders.unwrap_or_default(),
|
|||
};
|
|||
// TODO: check account data and, if different from config, update them
|
|||
Ok(ac)
|
|||
}
|
|||
}
|
|||
|
|||
pub fn init_account(cert: &Certificate) -> Result<(), Error> {
|
|||
if !storage::account_files_exists(&cert.file_manager) {
|
|||
cert.info(&format!(
|
|||
"Account {} does not exists. Creating it.",
|
|||
&cert.account.name
|
|||
));
|
|||
let key_pair = gen_keypair(cert.account.key_type)?;
|
|||
storage::set_account_keypair(&cert.file_manager, &key_pair)?;
|
|||
cert.debug(&format!("Account {} created.", &cert.account.name));
|
|||
} else {
|
|||
let key_pair = storage::get_account_keypair(&cert.file_manager)?;
|
|||
if key_pair.key_type != cert.account.key_type {
|
|||
cert.info(&format!("Account {name} has a key pair of type {kt_has} while {kt_want} was expected. Creating a new {kt_want} key pair.", name=&cert.account.name, kt_has=key_pair.key_type, kt_want=cert.account.key_type));
|
|||
// TODO: Do a propper key rollover
|
|||
let key_pair = gen_keypair(cert.account.key_type)?;
|
|||
storage::set_account_keypair(&cert.file_manager, &key_pair)?;
|
|||
cert.debug(&format!(
|
|||
"Account {} updated with a new {} key pair.",
|
|||
&cert.account.name, cert.account.key_type
|
|||
account: &mut BaseAccount,
|
|||
) -> Result<(), Error> {
|
|||
account.debug(&format!(
|
|||
"creating account on endpoint {}...",
|
|||
&endpoint.name
|
|||
));
|
|||
} else {
|
|||
cert.debug(&format!("Account {} already exists.", &cert.account.name));
|
|||
}
|
|||
}
|
|||
let account_struct = Account::new(account, endpoint);
|
|||
let account_struct = serde_json::to_string(&account_struct)?;
|
|||
let acc_ref = &account_struct;
|
|||
let kp_ref = &account.current_key.key;
|
|||
let signature_algorithm = &account.current_key.signature_algorithm;
|
|||
let data_builder =
|
|||
|n: &str, url: &str| encode_jwk(kp_ref, signature_algorithm, acc_ref.as_bytes(), url, n);
|
|||
let (acc_rep, account_url) = http::new_account(endpoint, root_certs, &data_builder)?;
|
|||
account.set_account_url(&endpoint.name, &account_url)?;
|
|||
let msg = format!(
|
|||
"endpoint {}: account {}: the server has not provided an order URL upon account creation",
|
|||
&endpoint.name, &account.name
|
|||
);
|
|||
let order_url = acc_rep.orders.ok_or_else(|| Error::from(&msg))?;
|
|||
account.set_order_url(&endpoint.name, &order_url)?;
|
|||
account.update_key_hash(&endpoint.name)?;
|
|||
account.update_contacts_hash(&endpoint.name)?;
|
|||
account.save()?;
|
|||
account.info(&format!("account created on endpoint {}", &endpoint.name));
|
|||
Ok(())
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue