use urn::Urn;
use crate::{
constants::{
DEFAULT_ARCHIVE_VAULT_NAME, DEFAULT_AUTHENTICATOR_VAULT_NAME,
DEFAULT_CONTACTS_VAULT_NAME, FILE_PASSWORD_URN,
},
crypto::AccessKey,
encode,
storage::AppPaths,
vault::{
secret::{Secret, SecretMeta, UserData},
Gatekeeper, Summary, Vault, VaultBuilder, VaultFlags,
},
vfs, Result,
};
use web3_address::ethereum::Address;
use super::{DelegatedPassphrase, Identity, UserIdentity};
use secrecy::SecretString;
pub struct NewAccount {
pub address: Address,
pub user: UserIdentity,
pub default_vault: Vault,
pub archive: Option<Vault>,
pub authenticator: Option<Vault>,
pub contacts: Option<Vault>,
}
pub struct ImportedAccount {
pub summary: Summary,
pub archive: Option<Summary>,
pub authenticator: Option<Summary>,
pub contacts: Option<Summary>,
}
pub struct AccountBuilder {
account_name: String,
passphrase: SecretString,
save_passphrase: bool,
create_archive: bool,
create_authenticator: bool,
create_contacts: bool,
create_file_password: bool,
default_folder_name: Option<String>,
}
impl AccountBuilder {
pub fn new(account_name: String, passphrase: SecretString) -> Self {
Self {
account_name,
passphrase,
save_passphrase: false,
create_archive: false,
create_authenticator: false,
create_contacts: false,
create_file_password: false,
default_folder_name: None,
}
}
pub fn save_passphrase(mut self, value: bool) -> Self {
self.save_passphrase = value;
self
}
pub fn create_archive(mut self, value: bool) -> Self {
self.create_archive = value;
self
}
pub fn create_authenticator(mut self, value: bool) -> Self {
self.create_authenticator = value;
self
}
pub fn create_contacts(mut self, value: bool) -> Self {
self.create_contacts = value;
self
}
pub fn create_file_password(mut self, value: bool) -> Self {
self.create_file_password = value;
self
}
pub fn default_folder_name(mut self, value: Option<String>) -> Self {
self.default_folder_name = value;
self
}
pub async fn build(self) -> Result<(Vault, NewAccount)> {
let AccountBuilder {
account_name,
passphrase,
save_passphrase,
create_archive,
create_authenticator,
create_contacts,
create_file_password,
mut default_folder_name,
} = self;
let (address, identity_vault) = Identity::new_login_vault(
account_name.clone(),
passphrase.clone(),
)
.await?;
let buffer = encode(&identity_vault).await?;
let user =
Identity::login_buffer(buffer, passphrase.clone(), None, None)
.await?;
let vault_passphrase =
DelegatedPassphrase::generate_vault_passphrase()?;
let mut builder = VaultBuilder::new().flags(VaultFlags::DEFAULT);
if let Some(name) = default_folder_name.take() {
builder = builder.public_name(name);
}
let mut default_vault =
builder.password(vault_passphrase.clone(), None).await?;
if save_passphrase {
let mut keeper = Gatekeeper::new(default_vault, None);
keeper.unlock(vault_passphrase.clone().into()).await?;
let secret = Secret::Account {
account: account_name,
password: passphrase.clone(),
url: None,
user_data: UserData::new_comment(address.to_string()),
};
let mut meta = SecretMeta::new(
"Account Password".to_string(),
secret.kind(),
);
meta.set_favorite(true);
keeper.create(meta, secret).await?;
default_vault = keeper.into();
}
let mut keeper = Gatekeeper::new(identity_vault, None);
keeper.unlock(passphrase.into()).await?;
DelegatedPassphrase::save_vault_passphrase(
&mut keeper,
default_vault.id(),
AccessKey::Password(vault_passphrase),
)
.await?;
if create_file_password {
let file_passphrase =
DelegatedPassphrase::generate_vault_passphrase()?;
let secret = Secret::Password {
password: file_passphrase,
name: None,
user_data: UserData::new_comment(address.to_string()),
};
let mut meta =
SecretMeta::new("File Encryption".to_string(), secret.kind());
let urn: Urn = FILE_PASSWORD_URN.parse()?;
meta.set_urn(Some(urn));
keeper.create(meta, secret).await?;
}
let archive = if create_archive {
let archive_passphrase =
DelegatedPassphrase::generate_vault_passphrase()?;
let vault = VaultBuilder::new()
.public_name(DEFAULT_ARCHIVE_VAULT_NAME.to_string())
.flags(VaultFlags::ARCHIVE)
.password(archive_passphrase.clone(), None)
.await?;
DelegatedPassphrase::save_vault_passphrase(
&mut keeper,
vault.id(),
AccessKey::Password(archive_passphrase),
)
.await?;
Some(vault)
} else {
None
};
let authenticator = if create_authenticator {
let auth_passphrase =
DelegatedPassphrase::generate_vault_passphrase()?;
let vault = VaultBuilder::new()
.public_name(DEFAULT_AUTHENTICATOR_VAULT_NAME.to_string())
.flags(VaultFlags::AUTHENTICATOR | VaultFlags::NO_SYNC_SELF)
.password(auth_passphrase.clone(), None)
.await?;
DelegatedPassphrase::save_vault_passphrase(
&mut keeper,
vault.id(),
AccessKey::Password(auth_passphrase),
)
.await?;
Some(vault)
} else {
None
};
let contacts = if create_contacts {
let auth_passphrase =
DelegatedPassphrase::generate_vault_passphrase()?;
let vault = VaultBuilder::new()
.public_name(DEFAULT_CONTACTS_VAULT_NAME.to_string())
.flags(VaultFlags::CONTACT)
.password(auth_passphrase.clone(), None)
.await?;
DelegatedPassphrase::save_vault_passphrase(
&mut keeper,
vault.id(),
AccessKey::Password(auth_passphrase),
)
.await?;
Some(vault)
} else {
None
};
Ok((
keeper.into(),
NewAccount {
address,
user,
default_vault,
archive,
authenticator,
contacts,
},
))
}
pub async fn write(
identity_vault: Vault,
account: NewAccount,
) -> Result<NewAccount> {
let address = account.address.to_string();
let identity_vault_file = AppPaths::identity_vault(&address)?;
let buffer = encode(&identity_vault).await?;
vfs::write(identity_vault_file, buffer).await?;
AppPaths::files_dir(&address)?;
Ok(account)
}
pub async fn finish(self) -> Result<NewAccount> {
let (identity_vault, account) = self.build().await?;
Self::write(identity_vault, account).await
}
}