use crate::{
device::DeviceManager, DelegatedAccess, Error, IdentityFolder,
PublicIdentity, Result,
};
use async_trait::async_trait;
use sos_backend::BackendTarget;
use sos_core::{
crypto::AccessKey, AccountId, AuthenticationError, SecretId, VaultId,
};
use std::collections::HashMap;
use urn::Urn;
#[cfg(feature = "files")]
use secrecy::SecretString;
pub struct FolderKeys(pub HashMap<VaultId, AccessKey>);
impl FolderKeys {
pub fn find(&self, id: &VaultId) -> Option<&AccessKey> {
self.0
.iter()
.find_map(|(k, v)| if k == id { Some(v) } else { None })
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl DelegatedAccess for FolderKeys {
type Error = Error;
async fn find_folder_password(
&self,
folder_id: &VaultId,
) -> Result<Option<AccessKey>> {
Ok(self.find(folder_id).cloned())
}
async fn remove_folder_password(
&mut self,
folder_id: &VaultId,
) -> Result<()> {
self.0.remove(folder_id);
Ok(())
}
async fn save_folder_password(
&mut self,
folder_id: &VaultId,
key: AccessKey,
) -> Result<()> {
self.0.insert(*folder_id, key);
Ok(())
}
}
pub type UrnLookup = HashMap<(VaultId, Urn), SecretId>;
pub struct Identity {
target: BackendTarget,
account: Option<PublicIdentity>,
identity: Option<IdentityFolder>,
}
impl Identity {
pub fn new(target: BackendTarget) -> Self {
Self {
target,
identity: None,
account: None,
}
}
pub fn devices(&self) -> Result<&DeviceManager> {
Ok(self
.identity
.as_ref()
.ok_or(AuthenticationError::NotAuthenticated)?
.devices()?)
}
pub fn account(&self) -> Result<&PublicIdentity> {
Ok(self
.account
.as_ref()
.ok_or(AuthenticationError::NotAuthenticated)?)
}
fn account_mut(&mut self) -> Result<&mut PublicIdentity> {
Ok(self
.account
.as_mut()
.ok_or(AuthenticationError::NotAuthenticated)?)
}
pub fn identity(&self) -> Result<&IdentityFolder> {
Ok(self
.identity
.as_ref()
.ok_or(AuthenticationError::NotAuthenticated)?)
}
#[doc(hidden)]
pub fn identity_mut(&mut self) -> Result<&mut IdentityFolder> {
Ok(self
.identity
.as_mut()
.ok_or(AuthenticationError::NotAuthenticated)?)
}
pub async fn verify(&self, key: &AccessKey) -> bool {
if let Some(identity) = &self.identity {
identity.verify(key).await
} else {
false
}
}
pub async fn rename_account(
&mut self,
account_name: String,
) -> Result<()> {
self.identity_mut()?.rename(account_name.clone()).await?;
self.account_mut()?.set_label(account_name);
Ok(())
}
#[cfg(feature = "files")]
pub async fn create_file_encryption_password(&mut self) -> Result<()> {
self.identity_mut()?.create_file_encryption_password().await
}
#[cfg(feature = "files")]
pub async fn find_file_encryption_password(
&self,
) -> Result<SecretString> {
self.identity()?.find_file_encryption_password().await
}
pub async fn login(
&mut self,
account_id: &AccountId,
key: &AccessKey,
) -> Result<()> {
let target = self.target.clone().with_account_id(account_id);
let mut identity =
IdentityFolder::login(&target, account_id, key).await?;
identity.ensure_device_vault(target).await?;
self.identity = Some(identity);
Ok(())
}
pub async fn sign_in(
&mut self,
account_id: &AccountId,
key: &AccessKey,
) -> Result<()> {
let accounts = self.target.list_accounts().await?;
let account = accounts
.into_iter()
.find(|a| a.account_id() == account_id)
.ok_or_else(|| Error::NoAccount(account_id.to_string()))?;
tracing::debug!("identity::sign_in");
self.login(account_id, key).await?;
tracing::debug!("identity::verified");
self.account = Some(account);
Ok(())
}
pub async fn sign_out(&mut self) -> Result<()> {
tracing::debug!("identity::sign_out");
self.identity_mut()?.sign_out().await?;
self.account = None;
self.identity = None;
Ok(())
}
}
impl From<Identity> for BackendTarget {
fn from(value: Identity) -> Self {
value.target
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl DelegatedAccess for Identity {
type Error = Error;
async fn find_folder_password(
&self,
folder_id: &VaultId,
) -> Result<Option<AccessKey>> {
self.identity()?.find_folder_password(folder_id).await
}
async fn remove_folder_password(
&mut self,
folder_id: &VaultId,
) -> Result<()> {
self.identity_mut()?.remove_folder_password(folder_id).await
}
async fn save_folder_password(
&mut self,
folder_id: &VaultId,
key: AccessKey,
) -> Result<()> {
self.identity_mut()?
.save_folder_password(folder_id, key)
.await
}
}