use crate::error::{KeyringError, Result};
use crate::Keyring;
use dbus_secret_service::{Collection, EncryptionType, SecretService};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub struct LinuxKeyringManager {
pub ss: Arc<Mutex<SecretService>>,
pub application: String,
}
impl LinuxKeyringManager {
pub fn new(application: &str) -> Result<Self> {
let s = Self {
ss: Arc::new(Mutex::new(SecretService::connect(EncryptionType::Dh)?)),
application: application.to_owned(),
};
s.with_keyring("test_service", "test_key", |k| {
if let Err(e) = k.set_value("test_value") {
return Err(KeyringError::Generic(format!(
"Keyring is not setting values: {}",
e
)));
}
match k.get_value() {
Ok(v) => {
if v != "test_value" {
return Err(KeyringError::Generic(
"Keyring is not returning correct values".to_owned(),
));
}
}
Err(e) => {
return Err(KeyringError::Generic(format!(
"Keyring is not operational: {}",
e
)));
}
};
if let Err(e) = k.delete_value() {
return Err(KeyringError::Generic(format!(
"Keyring is not deleting values: {}",
e
)));
}
Ok(())
})?;
Ok(s)
}
pub fn with_keyring<F, T>(&self, service: &str, key: &str, func: F) -> Result<T>
where
F: FnOnce(&mut dyn Keyring) -> Result<T>,
{
let ss = self
.ss
.lock()
.map_err(|e| KeyringError::Generic(format!("Failed to lock secret service: {}", e)))?;
let coll = match ss.get_default_collection() {
Ok(c) => c,
Err(_) => ss.create_collection("Default", "default")?,
};
let label = &format!(
"Password for {} on {} in {}",
key, service, &self.application
)[..];
let mut attributes: HashMap<&str, &str> = HashMap::new();
attributes.insert("service", service);
attributes.insert("username", key);
attributes.insert("application", &self.application);
let mut kr = LinuxKeyring::new(coll, label, attributes)?;
func(&mut kr)
}
}
pub struct LinuxKeyring<'a> {
collection: Collection<'a>,
label: &'a str,
attributes: HashMap<&'a str, &'a str>,
}
impl<'a> LinuxKeyring<'a> {
fn new(
collection: Collection<'a>,
label: &'a str,
attributes: HashMap<&'a str, &'a str>,
) -> Result<LinuxKeyring<'a>> {
Ok(LinuxKeyring {
collection,
label,
attributes,
})
}
}
impl<'a> Keyring for LinuxKeyring<'a> {
fn set_value(&mut self, value: &str) -> Result<()> {
self.collection.create_item(
self.label,
self.attributes.clone(),
value.as_bytes(),
true, "text/plain",
)?;
Ok(())
}
fn get_value(&self) -> Result<String> {
let search = self.collection.search_items(self.attributes.clone())?;
let item = search.first().ok_or(KeyringError::NoPasswordFound)?;
let secret_bytes = item.get_secret()?;
let secret = String::from_utf8(secret_bytes)?;
Ok(secret)
}
fn delete_value(&mut self) -> Result<()> {
let search = self.collection.search_items(self.attributes.clone())?;
let item = search.first().ok_or(KeyringError::NoPasswordFound)?;
item.delete().map_err(KeyringError::SecretServiceError)
}
}