use askar_storage::backend::copy_profile;
use crate::{
error::Error,
kms::{KeyEntry, KeyParams, KmsCategory, LocalKey},
storage::{
any::{AnyBackend, AnyBackendSession},
backend::{Backend, BackendSession, ManageBackend},
entry::{Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter},
generate_raw_store_key,
},
};
pub use crate::storage::{entry, PassKey, StoreKeyMethod};
#[derive(Debug, Clone)]
pub struct Store(AnyBackend);
impl Store {
pub(crate) fn new(inner: AnyBackend) -> Self {
Self(inner)
}
pub async fn provision(
db_url: &str,
key_method: StoreKeyMethod,
pass_key: PassKey<'_>,
profile: Option<String>,
recreate: bool,
) -> Result<Self, Error> {
let backend = db_url
.provision_backend(key_method, pass_key, profile, recreate)
.await?;
Ok(Self::new(backend))
}
pub async fn open(
db_url: &str,
key_method: Option<StoreKeyMethod>,
pass_key: PassKey<'_>,
profile: Option<String>,
) -> Result<Self, Error> {
let backend = db_url.open_backend(key_method, pass_key, profile).await?;
Ok(Self::new(backend))
}
pub async fn remove(db_url: &str) -> Result<bool, Error> {
Ok(db_url.remove_backend().await?)
}
pub fn new_raw_key(seed: Option<&[u8]>) -> Result<PassKey<'static>, Error> {
Ok(generate_raw_store_key(seed)?)
}
pub fn get_active_profile(&self) -> String {
self.0.get_active_profile()
}
pub async fn get_default_profile(&self) -> Result<String, Error> {
Ok(self.0.get_default_profile().await?)
}
pub async fn set_default_profile(&self, profile: String) -> Result<(), Error> {
Ok(self.0.set_default_profile(profile).await?)
}
pub async fn rekey(
&mut self,
method: StoreKeyMethod,
pass_key: PassKey<'_>,
) -> Result<(), Error> {
Ok(self.0.rekey(method, pass_key).await?)
}
pub async fn copy_to(
&self,
target_url: &str,
key_method: StoreKeyMethod,
pass_key: PassKey<'_>,
recreate: bool,
) -> Result<Self, Error> {
let default_profile = self.get_default_profile().await?;
let profile_ids = self.list_profiles().await?;
let target = target_url
.provision_backend(key_method, pass_key, Some(default_profile), recreate)
.await?;
for profile in profile_ids {
copy_profile(&self.0, &target, &profile, &profile).await?;
}
Ok(Self::new(target))
}
pub async fn create_profile(&self, name: Option<String>) -> Result<String, Error> {
Ok(self.0.create_profile(name).await?)
}
pub async fn list_profiles(&self) -> Result<Vec<String>, Error> {
Ok(self.0.list_profiles().await?)
}
pub async fn remove_profile(&self, name: String) -> Result<bool, Error> {
Ok(self.0.remove_profile(name).await?)
}
pub async fn scan(
&self,
profile: Option<String>,
category: Option<String>,
tag_filter: Option<TagFilter>,
offset: Option<i64>,
limit: Option<i64>,
) -> Result<Scan<'static, Entry>, Error> {
Ok(self
.0
.scan(
profile,
Some(EntryKind::Item),
category,
tag_filter,
offset,
limit,
)
.await?)
}
pub async fn session(&self, profile: Option<String>) -> Result<Session, Error> {
let mut sess = Session::new(self.0.session(profile, false)?);
if let Err(e) = sess.ping().await {
sess.0.close(false).await?;
Err(e)
} else {
Ok(sess)
}
}
pub async fn transaction(&self, profile: Option<String>) -> Result<Session, Error> {
let mut txn = Session::new(self.0.session(profile, true)?);
if let Err(e) = txn.ping().await {
txn.0.close(false).await?;
Err(e)
} else {
Ok(txn)
}
}
pub async fn close(self) -> Result<(), Error> {
Ok(self.0.close().await?)
}
}
impl From<AnyBackend> for Store {
fn from(backend: AnyBackend) -> Self {
Self::new(backend)
}
}
#[derive(Debug)]
pub struct Session(AnyBackendSession);
impl Session {
pub(crate) fn new(inner: AnyBackendSession) -> Self {
Self(inner)
}
pub async fn count(
&mut self,
category: Option<&str>,
tag_filter: Option<TagFilter>,
) -> Result<i64, Error> {
Ok(self
.0
.count(Some(EntryKind::Item), category, tag_filter)
.await?)
}
pub async fn fetch(
&mut self,
category: &str,
name: &str,
for_update: bool,
) -> Result<Option<Entry>, Error> {
Ok(self
.0
.fetch(EntryKind::Item, category, name, for_update)
.await?)
}
pub async fn fetch_all(
&mut self,
category: Option<&str>,
tag_filter: Option<TagFilter>,
limit: Option<i64>,
for_update: bool,
) -> Result<Vec<Entry>, Error> {
Ok(self
.0
.fetch_all(
Some(EntryKind::Item),
category,
tag_filter,
limit,
for_update,
)
.await?)
}
pub async fn insert(
&mut self,
category: &str,
name: &str,
value: &[u8],
tags: Option<&[EntryTag]>,
expiry_ms: Option<i64>,
) -> Result<(), Error> {
Ok(self
.0
.update(
EntryKind::Item,
EntryOperation::Insert,
category,
name,
Some(value),
tags,
expiry_ms,
)
.await?)
}
pub async fn remove(&mut self, category: &str, name: &str) -> Result<(), Error> {
Ok(self
.0
.update(
EntryKind::Item,
EntryOperation::Remove,
category,
name,
None,
None,
None,
)
.await?)
}
pub async fn replace(
&mut self,
category: &str,
name: &str,
value: &[u8],
tags: Option<&[EntryTag]>,
expiry_ms: Option<i64>,
) -> Result<(), Error> {
Ok(self
.0
.update(
EntryKind::Item,
EntryOperation::Replace,
category,
name,
Some(value),
tags,
expiry_ms,
)
.await?)
}
pub async fn remove_all(
&mut self,
category: Option<&str>,
tag_filter: Option<TagFilter>,
) -> Result<i64, Error> {
Ok(self
.0
.remove_all(Some(EntryKind::Item), category, tag_filter)
.await?)
}
pub async fn update(
&mut self,
operation: EntryOperation,
category: &str,
name: &str,
value: Option<&[u8]>,
tags: Option<&[EntryTag]>,
expiry_ms: Option<i64>,
) -> Result<(), Error> {
Ok(self
.0
.update(
EntryKind::Item,
operation,
category,
name,
value,
tags,
expiry_ms,
)
.await?)
}
pub async fn insert_key(
&mut self,
name: &str,
key: &LocalKey,
metadata: Option<&str>,
tags: Option<&[EntryTag]>,
expiry_ms: Option<i64>,
) -> Result<(), Error> {
let data = key.encode()?;
let params = KeyParams {
metadata: metadata.map(str::to_string),
reference: None,
data: Some(data),
};
let value = params.to_bytes()?;
let mut ins_tags = Vec::with_capacity(10);
let alg = key.algorithm().as_str();
if !alg.is_empty() {
ins_tags.push(EntryTag::Encrypted("alg".to_string(), alg.to_string()));
}
let thumbs = key.to_jwk_thumbprints()?;
for thumb in thumbs {
ins_tags.push(EntryTag::Encrypted("thumb".to_string(), thumb));
}
if let Some(tags) = tags {
for t in tags {
ins_tags.push(t.map_ref(|k, v| (format!("user:{}", k), v.to_string())));
}
}
self.0
.update(
EntryKind::Kms,
EntryOperation::Insert,
KmsCategory::CryptoKey.as_str(),
name,
Some(value.as_ref()),
Some(ins_tags.as_slice()),
expiry_ms,
)
.await?;
Ok(())
}
pub async fn fetch_key(
&mut self,
name: &str,
for_update: bool,
) -> Result<Option<KeyEntry>, Error> {
Ok(
if let Some(row) = self
.0
.fetch(
EntryKind::Kms,
KmsCategory::CryptoKey.as_str(),
name,
for_update,
)
.await?
{
Some(KeyEntry::from_entry(row)?)
} else {
None
},
)
}
pub async fn fetch_all_keys(
&mut self,
algorithm: Option<&str>,
thumbprint: Option<&str>,
tag_filter: Option<TagFilter>,
limit: Option<i64>,
for_update: bool,
) -> Result<Vec<KeyEntry>, Error> {
let mut query_parts = Vec::with_capacity(3);
if let Some(query) = tag_filter.map(|f| f.into_query()) {
query_parts.push(TagFilter::from(
query
.map_names(|mut k| {
k.replace_range(0..0, "user:");
Result::<_, ()>::Ok(k)
})
.unwrap(),
));
}
if let Some(algorithm) = algorithm {
query_parts.push(TagFilter::is_eq("alg", algorithm));
}
if let Some(thumbprint) = thumbprint {
query_parts.push(TagFilter::is_eq("thumb", thumbprint));
}
let tag_filter = if query_parts.is_empty() {
None
} else {
Some(TagFilter::all_of(query_parts))
};
let rows = self
.0
.fetch_all(
Some(EntryKind::Kms),
Some(KmsCategory::CryptoKey.as_str()),
tag_filter,
limit,
for_update,
)
.await?;
let mut entries = Vec::with_capacity(rows.len());
for row in rows {
entries.push(KeyEntry::from_entry(row)?)
}
Ok(entries)
}
pub async fn remove_key(&mut self, name: &str) -> Result<(), Error> {
Ok(self
.0
.update(
EntryKind::Kms,
EntryOperation::Remove,
KmsCategory::CryptoKey.as_str(),
name,
None,
None,
None,
)
.await?)
}
pub async fn update_key(
&mut self,
name: &str,
metadata: Option<&str>,
tags: Option<&[EntryTag]>,
expiry_ms: Option<i64>,
) -> Result<(), Error> {
let row = self
.0
.fetch(EntryKind::Kms, KmsCategory::CryptoKey.as_str(), name, true)
.await?
.ok_or_else(|| err_msg!(NotFound, "Key entry not found"))?;
let mut params = KeyParams::from_slice(&row.value)?;
params.metadata = metadata.map(str::to_string);
let value = params.to_bytes()?;
let mut upd_tags = Vec::with_capacity(10);
if let Some(tags) = tags {
for t in tags {
upd_tags.push(t.map_ref(|k, v| (format!("user:{}", k), v.to_string())));
}
}
for t in row.tags {
if !t.name().starts_with("user:") {
upd_tags.push(t);
}
}
self.0
.update(
EntryKind::Kms,
EntryOperation::Replace,
KmsCategory::CryptoKey.as_str(),
name,
Some(value.as_ref()),
Some(upd_tags.as_slice()),
expiry_ms,
)
.await?;
Ok(())
}
pub async fn ping(&mut self) -> Result<(), Error> {
Ok(self.0.ping().await?)
}
pub async fn commit(mut self) -> Result<(), Error> {
Ok(self.0.close(true).await?)
}
pub async fn rollback(mut self) -> Result<(), Error> {
Ok(self.0.close(false).await?)
}
}