use std::{collections::HashMap, sync::Arc, time::Duration};
#[cfg(feature = "async-std")]
use async_std::sync::RwLock;
#[cfg(feature = "tokio")]
use tokio::sync::RwLock;
use zeroize::Zeroizing;
use super::{api, Algorithm, Error};
use crate::{crypto, Key};
#[derive(Debug)]
pub struct Item<'a> {
inner: Arc<api::Item<'a>>,
session: Arc<api::Session<'a>>,
service: Arc<api::Service<'a>>,
algorithm: Algorithm,
available: RwLock<bool>,
aes_key: Option<Arc<Key>>,
}
impl<'a> Item<'a> {
pub(crate) fn new(
service: Arc<api::Service<'a>>,
session: Arc<api::Session<'a>>,
algorithm: Algorithm,
item: api::Item<'a>,
aes_key: Option<Arc<Key>>,
) -> Item<'a> {
Self {
inner: Arc::new(item),
service,
session,
algorithm,
available: RwLock::new(true),
aes_key,
}
}
pub(crate) async fn is_available(&self) -> bool {
*self.available.read().await
}
pub async fn is_locked(&self) -> Result<bool, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.is_locked().await
}
}
pub async fn label(&self) -> Result<String, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.label().await
}
}
pub async fn set_label(&self, label: &str) -> Result<(), Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.set_label(label).await
}
}
pub async fn created(&self) -> Result<Duration, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.created().await
}
}
pub async fn modified(&self) -> Result<Duration, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.modified().await
}
}
pub async fn attributes(&self) -> Result<HashMap<String, String>, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.attributes().await
}
}
pub async fn set_attributes(&self, attributes: HashMap<&str, &str>) -> Result<(), Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.set_attributes(attributes).await
}
}
pub async fn delete(&self) -> Result<(), Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.inner.delete().await?;
*self.available.write().await = false;
Ok(())
}
}
pub async fn secret(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
let secret = self.inner.secret(&self.session).await?;
let value = match self.algorithm {
Algorithm::Plain => Zeroizing::new(secret.value.to_owned()),
Algorithm::Encrypted => {
let iv = &secret.parameters;
let aes_key = self.aes_key.as_ref().unwrap();
crypto::decrypt(&secret.value, aes_key, iv)
}
};
Ok(value)
}
}
#[doc(alias = "SetSecret")]
pub async fn set_secret(
&self,
secret: impl AsRef<[u8]>,
content_type: &str,
) -> Result<(), Error> {
let secret = match self.algorithm {
Algorithm::Plain => api::Secret::new(Arc::clone(&self.session), secret, content_type),
Algorithm::Encrypted => {
let aes_key = self.aes_key.as_ref().unwrap();
api::Secret::new_encrypted(Arc::clone(&self.session), secret, content_type, aes_key)
}
};
self.inner.set_secret(&secret).await?;
Ok(())
}
pub async fn unlock(&self) -> Result<(), Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.service.unlock(&[self.inner.inner().path()]).await?;
Ok(())
}
}
pub async fn lock(&self) -> Result<(), Error> {
if !self.is_available().await {
Err(Error::Deleted)
} else {
self.service.lock(&[self.inner.inner().path()]).await?;
Ok(())
}
}
}