use super::error::KeyStoreError;
use keyring_core::Error::NoStorageAccess;
use keyring_core::api::CredentialApi;
use keyring_core::{Credential, Error};
use linux_keyutils::{KeyRing, KeyRingIdentifier};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Cred {
pub session: KeyRing,
pub persistent: Option<KeyRing>,
pub description: String,
pub specifiers: Option<(String, String)>,
}
impl CredentialApi for Cred {
fn set_secret(&self, secret: &[u8]) -> keyring_core::error::Result<()> {
if secret.is_empty() {
return Err(Error::Invalid(
"secret".to_string(),
"cannot be empty".to_string(),
));
}
self.set(secret)?;
Ok(())
}
fn get_secret(&self) -> keyring_core::error::Result<Vec<u8>> {
let buffer = self.get()?;
Ok(buffer)
}
fn delete_credential(&self) -> keyring_core::error::Result<()> {
self.remove()?;
Ok(())
}
fn get_credential(&self) -> keyring_core::Result<Option<Arc<Credential>>> {
self.session
.search(&self.description)
.map_err(KeyStoreError::from)
.map_err(keyring_core::Error::from)?;
Ok(None)
}
fn get_specifiers(&self) -> Option<(String, String)> {
self.specifiers.clone()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl Cred {
pub fn build_from_specifiers(
target: Option<&str>,
delimiters: &[String; 3],
service_no_dividers: bool,
service: &str,
user: &str,
) -> keyring_core::error::Result<Self> {
let (description, specifiers) = match target {
Some(value) => (value.to_string(), None),
None => {
if service_no_dividers && service.contains(delimiters[1].as_str()) {
return Err(Error::Invalid(
"service".to_string(),
"cannot contain delimiter".to_string(),
));
}
(
format!(
"{}{user}{}{service}{}",
delimiters[0], delimiters[1], delimiters[2]
),
Some((service.to_string(), user.to_string())),
)
}
};
if description.is_empty() {
return Err(Error::Invalid(
"description".to_string(),
"cannot be empty".to_string(),
));
}
let session = KeyRing::from_special_id(KeyRingIdentifier::Session, false)
.map_err(|e| NoStorageAccess(e.into()))?;
let persistent = KeyRing::get_persistent(KeyRingIdentifier::Session).ok();
Ok(Self {
session,
persistent,
description,
specifiers,
})
}
fn get(&self) -> Result<Vec<u8>, KeyStoreError> {
let key = self.session.search(&self.description)?;
self.session.link_key(key)?;
if let Some(keyring) = self.persistent {
keyring.link_key(key)?;
}
let data = key.read_to_vec()?;
Ok(data)
}
fn set<T: AsRef<[u8]>>(&self, secret: T) -> Result<(), KeyStoreError> {
let key = self.session.add_key(&self.description, &secret)?;
if let Some(keyring) = self.persistent {
keyring.link_key(key).map_err(KeyStoreError)?;
}
Ok(())
}
fn remove(&self) -> Result<(), KeyStoreError> {
let key = self.session.search(&self.description)?;
key.invalidate()?;
Ok(())
}
}