use crate::traits::private::Internal;
use crate::{
AccessOptions, ClientAccountStorage, ClientBaseStorage,
ClientFolderStorage, ClientSecretStorage, Error, Result,
StorageChangeEvent,
};
use async_trait::async_trait;
use sos_backend::StorageError;
use sos_core::{
events::{ReadEvent, WriteEvent},
AuthenticationError, SecretId, VaultCommit, VaultId,
};
use sos_vault::secret::{Secret, SecretMeta, SecretRow};
use sos_vault::Summary;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<T> ClientSecretStorage for T
where
T: ClientAccountStorage
+ ClientFolderStorage
+ ClientBaseStorage
+ Send
+ Sync,
{
async fn create_secret(
&mut self,
secret_data: SecretRow,
#[allow(unused_mut, unused_variables)] mut options: AccessOptions,
) -> Result<StorageChangeEvent> {
self.guard_authenticated(Internal)?;
let summary = if let Some(folder_id) = &options.folder {
self.find(|f| f.id() == folder_id)
.cloned()
.ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
} else {
self.current_folder().ok_or(Error::NoOpenVault)?
};
#[cfg(feature = "search")]
let index_doc = if let Some(index) = self.search_index() {
let search = index.search();
let index = search.read().await;
Some(index.prepare(
summary.id(),
secret_data.id(),
secret_data.meta(),
secret_data.secret(),
))
} else {
None
};
let event = {
let folder = self
.folders_mut()
.get_mut(summary.id())
.ok_or(StorageError::FolderNotFound(*summary.id()))?;
folder.create_secret(&secret_data).await?
};
#[cfg(feature = "files")]
let file_events = {
let (file_events, write_update) = self
.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.create_files(
&summary,
secret_data,
&mut options.file_progress,
)
.await?;
if let Some((id, secret_data)) = write_update {
self.write_secret(
&summary,
&id,
secret_data,
false,
Internal,
)
.await?;
}
file_events
};
let result = StorageChangeEvent {
event,
#[cfg(feature = "files")]
file_events,
};
#[cfg(feature = "files")]
self.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.append_file_mutation_events(&result.file_events)
.await?;
#[cfg(feature = "search")]
if let (Some(index), Some(index_doc)) =
(self.search_index(), index_doc)
{
let search = index.search();
let mut index = search.write().await;
index.commit(index_doc)
}
Ok(result)
}
async fn raw_secret(
&self,
folder_id: &VaultId,
secret_id: &SecretId,
) -> Result<Option<(VaultCommit, ReadEvent)>> {
self.guard_authenticated(Internal)?;
let folder = self
.folders()
.get(folder_id)
.ok_or(StorageError::FolderNotFound(*folder_id))?;
Ok(folder.raw_secret(secret_id).await?)
}
async fn read_secret(
&self,
id: &SecretId,
options: &AccessOptions,
) -> Result<(Summary, SecretMeta, Secret, ReadEvent)> {
self.guard_authenticated(Internal)?;
let summary = if let Some(folder_id) = &options.folder {
self.find(|f| f.id() == folder_id)
.cloned()
.ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
} else {
self.current_folder().ok_or(Error::NoOpenVault)?
};
let folder = self
.folders()
.get(summary.id())
.ok_or(StorageError::FolderNotFound(*summary.id()))?;
let result = folder
.read_secret(id)
.await?
.ok_or(Error::SecretNotFound(*id))?;
Ok((summary, result.0, result.1, result.2))
}
async fn update_secret(
&mut self,
secret_id: &SecretId,
meta: SecretMeta,
secret: Option<Secret>,
#[allow(unused_mut, unused_variables)] mut options: AccessOptions,
) -> Result<StorageChangeEvent> {
self.guard_authenticated(Internal)?;
let (folder, old_meta, old_secret, _) =
self.read_secret(secret_id, &options).await?;
let old_secret_data =
SecretRow::new(*secret_id, old_meta, old_secret);
let secret_data = if let Some(secret) = secret {
SecretRow::new(*secret_id, meta, secret)
} else {
let mut secret_data = old_secret_data.clone();
*secret_data.meta_mut() = meta;
secret_data
};
let event = self
.write_secret(
&folder,
secret_id,
secret_data.clone(),
true,
Internal,
)
.await?;
#[cfg(feature = "files")]
let file_events = {
let (file_events, write_update) = self
.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.update_files(
&folder,
&folder,
&old_secret_data,
secret_data,
&mut options.file_progress,
)
.await?;
if let Some((id, secret_data)) = write_update {
self.write_secret(&folder, &id, secret_data, false, Internal)
.await?;
}
file_events
};
let result = StorageChangeEvent {
event,
#[cfg(feature = "files")]
file_events,
};
#[cfg(feature = "files")]
self.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.append_file_mutation_events(&result.file_events)
.await?;
Ok(result)
}
async fn write_secret(
&mut self,
folder: &Summary,
id: &SecretId,
mut secret_data: SecretRow,
#[allow(unused_variables)] is_update: bool,
_: Internal,
) -> Result<WriteEvent> {
secret_data.meta_mut().touch();
#[cfg(feature = "search")]
let index_doc = if let Some(index) = self.search_index() {
let search = index.search();
let mut index = search.write().await;
if is_update {
index.remove(folder.id(), id);
}
Some(index.prepare(
folder.id(),
id,
secret_data.meta(),
secret_data.secret(),
))
} else {
None
};
let event = {
let folder = self
.folders_mut()
.get_mut(folder.id())
.ok_or(StorageError::FolderNotFound(*folder.id()))?;
let (_, meta, secret) = secret_data.into();
folder
.update_secret(id, meta, secret)
.await?
.ok_or(Error::SecretNotFound(*id))?
};
#[cfg(feature = "search")]
if let (Some(index), Some(index_doc)) =
(self.search_index(), index_doc)
{
let search = index.search();
let mut index = search.write().await;
index.commit(index_doc)
}
Ok(event)
}
async fn delete_secret(
&mut self,
secret_id: &SecretId,
#[allow(unused_mut, unused_variables)] mut options: AccessOptions,
) -> Result<StorageChangeEvent> {
self.guard_authenticated(Internal)?;
#[cfg(feature = "files")]
let (folder, secret_data) = {
let (folder, meta, secret, _) =
self.read_secret(secret_id, &options).await?;
(folder, SecretRow::new(*secret_id, meta, secret))
};
let event = self.remove_secret(secret_id, &options).await?;
let result = StorageChangeEvent {
event,
#[cfg(feature = "files")]
file_events: {
self.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.delete_files(
&folder,
&secret_data,
None,
&mut options.file_progress,
)
.await?
},
};
#[cfg(feature = "files")]
self.external_file_manager_mut()
.ok_or_else(|| AuthenticationError::NotAuthenticated)?
.append_file_mutation_events(&result.file_events)
.await?;
Ok(result)
}
async fn remove_secret(
&mut self,
id: &SecretId,
options: &AccessOptions,
) -> Result<WriteEvent> {
self.guard_authenticated(Internal)?;
let summary = if let Some(folder_id) = &options.folder {
self.find(|f| f.id() == folder_id)
.cloned()
.ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
} else {
self.current_folder().ok_or(Error::NoOpenVault)?
};
let event = {
let folder = self
.folders_mut()
.get_mut(summary.id())
.ok_or(StorageError::FolderNotFound(*summary.id()))?;
folder
.delete_secret(id)
.await?
.ok_or(Error::SecretNotFound(*id))?
};
#[cfg(feature = "search")]
if let Some(index) = self.search_index() {
let search = index.search();
let mut writer = search.write().await;
writer.remove(summary.id(), id);
}
Ok(event)
}
}