extern crate crypto;
extern crate dbus;
extern crate gmp;
extern crate num;
extern crate rand;
mod collection;
mod error;
mod item;
mod session;
mod ss;
mod ss_crypto;
mod util;
pub use collection::Collection;
pub use error::{Result, SsError};
pub use item::Item;
use util::{Interface, exec_prompt};
use session::Session;
pub use session::EncryptionType;
use ss::{
SS_DBUS_NAME,
SS_INTERFACE_SERVICE,
SS_PATH,
};
use dbus::{
BusName,
BusType,
Connection,
MessageItem,
Path,
};
use dbus::Interface as InterfaceName;
use dbus::MessageItem::{
Array,
DictEntry,
ObjectPath,
Str,
Variant,
};
use std::rc::Rc;
#[derive(Debug)]
pub struct SecretService {
bus: Rc<Connection>,
session: Session,
service_interface: Interface,
}
impl SecretService {
pub fn new(encryption: EncryptionType) -> ::Result<Self> {
let bus = Rc::new(try!(Connection::get_private(BusType::Session)));
let session = try!(Session::new(bus.clone(), encryption));
let service_interface = Interface::new(
bus.clone(),
BusName::new(SS_DBUS_NAME).unwrap(),
Path::new(SS_PATH).unwrap(),
InterfaceName::new(SS_INTERFACE_SERVICE).unwrap()
);
Ok(SecretService {
bus: bus.clone(),
session: session,
service_interface: service_interface,
})
}
pub fn get_all_collections(&self) -> ::Result<Vec<Collection>> {
let res = try!(self.service_interface.get_props("Collections"));
let collections: &Vec<_> = res.inner().unwrap();
Ok(collections.iter().map(|object_path| {
let path: &Path = object_path.inner().unwrap();
Collection::new(
self.bus.clone(),
&self.session,
path.clone()
)
}).collect::<Vec<_>>())
}
pub fn get_collection_by_alias(&self, alias: &str) -> ::Result<Collection>{
let name = Str(alias.to_owned());
let res = try!(self.service_interface.method("ReadAlias", vec![name]));
if let ObjectPath(ref path) = res[0] {
Ok(Collection::new(
self.bus.clone(),
&self.session,
path.clone()
))
} else {
Err(SsError::Parse)
}
}
pub fn get_default_collection(&self) -> ::Result<Collection> {
self.get_collection_by_alias("default")
}
pub fn get_any_collection(&self) -> ::Result<Collection> {
self.get_default_collection()
.or_else(|_| {
self.get_collection_by_alias("session")
}).or_else(|_| {
let collections = try!(self.get_all_collections());
collections
.get(0)
.ok_or(SsError::NoResult)
.map(|collection| collection.clone())
})
}
pub fn create_collection(&self, label: &str, alias: &str) -> ::Result<Collection> {
let label = DictEntry(
Box::new(Str("org.freedesktop.Secret.Collection.Label".to_owned())),
Box::new(Variant(Box::new(Str(label.to_owned()))))
);
let label_type_sig = label.type_sig();
let properties = Array(vec![label], label_type_sig);
let alias = Str(alias.to_owned());
let res = try!(self.service_interface.method("CreateCollection", vec![properties, alias]));
let collection_path: Path = {
let created_object_path = try!(res
.get(0)
.ok_or(SsError::NoResult)
);
let created_path: &Path = created_object_path.inner().unwrap();
if &**created_path == "/" {
let prompt_object_path = try!(res
.get(1)
.ok_or(SsError::NoResult)
);
let prompt_path: &Path = prompt_object_path.inner().unwrap();
let var_obj_path = try!(exec_prompt(self.bus.clone(), prompt_path.clone()));
let obj_path: &MessageItem = var_obj_path.inner().unwrap();
let path: &Path = obj_path.inner().unwrap();
path.clone()
} else {
created_path.clone()
}
};
Ok(Collection::new(
self.bus.clone(),
&self.session,
collection_path.clone()
))
}
pub fn search_items(&self, attributes: Vec<(&str, &str)>) -> ::Result<Vec<Item>> {
let attr_dict_entries: Vec<_> = attributes.iter().map(|&(key, value)| {
let dict_entry = (Str(key.to_owned()), Str(value.to_owned()));
MessageItem::from(dict_entry)
}).collect();
let attr_type_sig = DictEntry(
Box::new(Str("".to_owned())),
Box::new(Str("".to_owned()))
).type_sig();
let attr_dbus_dict = Array(
attr_dict_entries,
attr_type_sig
);
let res = try!(self.service_interface.method("SearchItems", vec![attr_dbus_dict]));
let mut unlocked = match res.get(0) {
Some(ref array) => {
match **array {
Array(ref v, _) => v.clone(),
_ => Vec::new(),
}
}
_ => Vec::new(),
};
let locked = match res.get(1) {
Some(ref array) => {
match **array {
Array(ref v, _) => v.clone(),
_ => Vec::new(),
}
}
_ => Vec::new(),
};
unlocked.extend(locked);
let items = unlocked;
Ok(items.iter().map(|item_path| {
let path: &Path = item_path.inner().unwrap();
Item::new(
self.bus.clone(),
&self.session,
path.clone()
)
}).collect::<Vec<_>>())
}
}
#[cfg(test)]
mod test {
use super::*;
use dbus::Path;
#[test]
fn should_create_secret_service() {
SecretService::new(EncryptionType::Plain).unwrap();
}
#[test]
fn should_get_all_collections() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
let collections = ss.get_all_collections().unwrap();
assert!(collections.len() >= 1);
println!("{:?}", collections);
println!("# of collections {:?}", collections.len());
}
#[test]
fn should_get_collection_by_alias() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
ss.get_collection_by_alias("session").unwrap();
}
#[test]
fn should_get_default_collection() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
ss.get_default_collection().unwrap();
}
#[test]
fn should_get_any_collection() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
let _ = ss.get_any_collection().unwrap();
}
#[test]
#[ignore]
fn should_create_and_delete_collection() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
let test_collection = ss.create_collection("Test", "").unwrap();
println!("{:?}", test_collection);
assert_eq!(
test_collection.collection_path,
Path::new("/org/freedesktop/secrets/collection/Test").unwrap()
);
test_collection.delete().unwrap();
}
#[test]
fn should_search_items() {
let ss = SecretService::new(EncryptionType::Plain).unwrap();
let collection = ss.get_default_collection().unwrap();
let item = collection.create_item(
"test",
vec![("test_attribute_in_ss", "test_value")],
b"test_secret",
false,
"text/plain"
).unwrap();
ss.search_items(Vec::new()).unwrap();
let bad_search = ss.search_items(vec![("test".into(), "test".into())]).unwrap();
assert_eq!(bad_search.len(), 0);
let search_item = ss.search_items(
vec![("test_attribute_in_ss", "test_value")]
).unwrap();
assert_eq!(
item.item_path,
search_item[0].item_path
);
item.delete().unwrap();
}
}