use super::{KeyTriple, ManageKeyIDs};
use crate::authenticators::ApplicationName;
use log::{error, info};
use parsec_interface::requests::ProviderID;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::ffi::OsStr;
use std::fs;
use std::fs::{DirEntry, File};
use std::io::{Error, ErrorKind, Read, Write};
use std::path::PathBuf;
pub const DEFAULT_MAPPINGS_PATH: &str = "./mappings";
#[derive(Debug)]
pub struct OnDiskKeyIDManager {
key_store: HashMap<KeyTriple, Vec<u8>>,
mappings_dir_path: PathBuf,
}
fn key_triple_to_base64_filenames(key_triple: &KeyTriple) -> (String, String, String) {
(
base64::encode_config(key_triple.app_name.get_name().as_bytes(), base64::URL_SAFE),
(key_triple.provider_id as u8).to_string(),
base64::encode_config(key_triple.key_name.as_bytes(), base64::URL_SAFE),
)
}
fn base64_data_to_string(base64_bytes: &[u8]) -> Result<String, String> {
match base64::decode_config(base64_bytes, base64::URL_SAFE) {
Ok(decode_bytes) => match String::from_utf8(decode_bytes) {
Ok(string) => Ok(string),
Err(error) => Err(error.to_string()),
},
Err(error) => Err(error.to_string()),
}
}
fn base64_data_triple_to_key_triple(
app_name: &[u8],
provider_id: ProviderID,
key_name: &[u8],
) -> Result<KeyTriple, String> {
let app_name = ApplicationName::new(base64_data_to_string(app_name)?);
let key_name = base64_data_to_string(key_name)?;
Ok(KeyTriple {
app_name,
provider_id,
key_name,
})
}
fn os_str_to_u8_ref(os_str: &OsStr) -> std::io::Result<&[u8]> {
match os_str.to_str() {
Some(str) => Ok(str.as_bytes()),
None => Err(Error::new(
ErrorKind::Other,
"Conversion from PathBuf to String failed.",
)),
}
}
fn os_str_to_provider_id(os_str: &OsStr) -> std::io::Result<ProviderID> {
match os_str.to_str() {
Some(str) => match str.parse::<u8>() {
Ok(provider_id_u8) => match ProviderID::try_from(provider_id_u8) {
Ok(provider_id) => Ok(provider_id),
Err(response_status) => {
Err(Error::new(ErrorKind::Other, response_status.to_string()))
}
},
Err(_) => Err(Error::new(
ErrorKind::Other,
"Failed to convert Provider directory name to an u8 number.",
)),
},
None => Err(Error::new(
ErrorKind::Other,
"Conversion from PathBuf to String failed.",
)),
}
}
fn list_dirs(path: &PathBuf) -> std::io::Result<Vec<PathBuf>> {
let dir_entries: std::io::Result<Vec<DirEntry>> = path.read_dir()?.collect();
Ok(dir_entries?
.iter()
.map(|dir_entry| dir_entry.path())
.filter(|dir_path| dir_path.is_dir())
.collect())
}
fn list_files(path: &PathBuf) -> std::io::Result<Vec<PathBuf>> {
let dir_entries: std::io::Result<Vec<DirEntry>> = path.read_dir()?.collect();
Ok(dir_entries?
.iter()
.map(|dir_entry| dir_entry.path())
.filter(|dir_path| dir_path.is_file())
.collect())
}
impl OnDiskKeyIDManager {
fn new(mappings_dir_path: PathBuf) -> std::io::Result<OnDiskKeyIDManager> {
let mut key_store = HashMap::new();
fs::create_dir_all(&mappings_dir_path)?;
for app_name_dir_path in list_dirs(&mappings_dir_path)?.iter() {
for provider_dir_path in list_dirs(&app_name_dir_path)?.iter() {
for key_name_file_path in list_files(&provider_dir_path)?.iter() {
info!("Found mapping file: {:?}.", key_name_file_path);
let mut key_id = Vec::new();
let mut key_id_file = File::open(&key_name_file_path)?;
let _ = key_id_file.read_to_end(&mut key_id)?;
match base64_data_triple_to_key_triple(
os_str_to_u8_ref(app_name_dir_path.file_name().expect(
"The application name directory path should contain a final component.",
))?,
os_str_to_provider_id(provider_dir_path.file_name().expect(
"The provider directory path should contain a final component.",
))?,
os_str_to_u8_ref(key_name_file_path.file_name().expect(
"The key name directory path should contain a final component.",
))?,
) {
Ok(key_triple) => {
let _ = key_store.insert(key_triple, key_id);
}
Err(string) => {
error!("Failed to convert the mapping path found to an UTF-8 string (error: {}).", string);
}
}
}
}
}
Ok(OnDiskKeyIDManager {
key_store,
mappings_dir_path,
})
}
fn save_mapping(&self, key_triple: &KeyTriple, key_id: &[u8]) -> std::io::Result<()> {
let (app_name, prov, key_name) = key_triple_to_base64_filenames(key_triple);
let provider_dir_path = self.mappings_dir_path.join(app_name).join(prov);
let key_name_file_path = provider_dir_path.join(key_name);
fs::create_dir_all(&provider_dir_path)?;
if key_name_file_path.exists() {
fs::remove_file(&key_name_file_path)?;
}
let mut mapping_file = fs::File::create(&key_name_file_path)?;
mapping_file.write_all(key_id)
}
fn delete_mapping(&self, key_triple: &KeyTriple) -> std::io::Result<()> {
let (app_name, prov, key_name) = key_triple_to_base64_filenames(key_triple);
let key_name_file_path = self
.mappings_dir_path
.join(app_name)
.join(prov)
.join(key_name);
if key_name_file_path.exists() {
fs::remove_file(key_name_file_path)
} else {
Ok(())
}
}
}
impl ManageKeyIDs for OnDiskKeyIDManager {
fn get(&self, key_triple: &KeyTriple) -> Result<Option<&[u8]>, String> {
if let Some(key_id) = self.key_store.get(key_triple) {
Ok(Some(key_id))
} else {
Ok(None)
}
}
fn get_all(&self, provider_id: ProviderID) -> Result<Vec<&KeyTriple>, String> {
Ok(self
.key_store
.keys()
.filter(|key_triple| key_triple.belongs_to_provider(provider_id))
.collect())
}
fn insert(
&mut self,
key_triple: KeyTriple,
key_id: Vec<u8>,
) -> Result<Option<Vec<u8>>, String> {
if let Err(err) = self.save_mapping(&key_triple, &key_id) {
Err(err.to_string())
} else {
Ok(self.key_store.insert(key_triple, key_id))
}
}
fn remove(&mut self, key_triple: &KeyTriple) -> Result<Option<Vec<u8>>, String> {
if let Err(err) = self.delete_mapping(key_triple) {
Err(err.to_string())
} else if let Some(key_id) = self.key_store.remove(key_triple) {
Ok(Some(key_id))
} else {
Ok(None)
}
}
fn exists(&self, key_triple: &KeyTriple) -> Result<bool, String> {
Ok(self.key_store.contains_key(key_triple))
}
}
#[derive(Debug, Default)]
pub struct OnDiskKeyIDManagerBuilder {
mappings_dir_path: Option<PathBuf>,
}
impl OnDiskKeyIDManagerBuilder {
pub fn new() -> OnDiskKeyIDManagerBuilder {
OnDiskKeyIDManagerBuilder {
mappings_dir_path: None,
}
}
pub fn with_mappings_dir_path(mut self, path: PathBuf) -> OnDiskKeyIDManagerBuilder {
self.mappings_dir_path = Some(path);
self
}
pub fn build(self) -> std::io::Result<OnDiskKeyIDManager> {
OnDiskKeyIDManager::new(self.mappings_dir_path.ok_or_else(|| {
error!("Mappings directory path is missing");
Error::new(ErrorKind::InvalidData, "mappings directory path is missing")
})?)
}
}
#[cfg(test)]
mod test {
use super::super::{KeyTriple, ManageKeyIDs};
use super::OnDiskKeyIDManager;
use crate::authenticators::ApplicationName;
use parsec_interface::requests::ProviderID;
use std::fs;
use std::path::PathBuf;
#[test]
fn insert_get_key_id() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/insert_get_key_id_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let key_triple = new_key_triple("insert_get_key_id".to_string());
let key_id = vec![0x11, 0x22, 0x33];
assert!(manager.get(&key_triple).unwrap().is_none());
assert!(manager
.insert(key_triple.clone(), key_id.clone())
.unwrap()
.is_none());
let stored_key_id = Vec::from(
manager
.get(&key_triple)
.unwrap()
.expect("Failed to get key id"),
);
assert_eq!(stored_key_id, key_id);
assert!(manager.remove(&key_triple).unwrap().is_some());
fs::remove_dir_all(path).unwrap();
}
#[test]
fn insert_remove_key() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/insert_remove_key_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let key_triple = new_key_triple("insert_remove_key".to_string());
let key_id = vec![0x11, 0x22, 0x33];
let _ = manager.insert(key_triple.clone(), key_id).unwrap();
assert!(manager.remove(&key_triple).unwrap().is_some());
fs::remove_dir_all(path).unwrap();
}
#[test]
fn remove_unexisting_key() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/remove_unexisting_key_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let key_triple = new_key_triple("remove_unexisting_key".to_string());
assert_eq!(manager.remove(&key_triple).unwrap(), None);
fs::remove_dir_all(path).unwrap();
}
#[test]
fn exists() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/exists_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let key_triple = new_key_triple("exists".to_string());
let key_id = vec![0x11, 0x22, 0x33];
assert!(!manager.exists(&key_triple).unwrap());
let _ = manager.insert(key_triple.clone(), key_id).unwrap();
assert!(manager.exists(&key_triple).unwrap());
let _ = manager.remove(&key_triple).unwrap();
assert!(!manager.exists(&key_triple).unwrap());
fs::remove_dir_all(path).unwrap();
}
#[test]
fn insert_overwrites() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/insert_overwrites_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let key_triple = new_key_triple("insert_overwrites".to_string());
let key_id_1 = vec![0x11, 0x22, 0x33];
let key_id_2 = vec![0xaa, 0xbb, 0xcc];
let _ = manager.insert(key_triple.clone(), key_id_1).unwrap();
let _ = manager
.insert(key_triple.clone(), key_id_2.clone())
.unwrap();
let stored_key_id = Vec::from(
manager
.get(&key_triple)
.unwrap()
.expect("Failed to get key id"),
);
assert_eq!(stored_key_id, key_id_2);
assert!(manager.remove(&key_triple).unwrap().is_some());
fs::remove_dir_all(path).unwrap();
}
#[test]
fn big_names_ascii() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/big_names_ascii_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let big_app_name_ascii = ApplicationName::new(" Lorem ipsum dolor sit amet, ei suas viris sea, deleniti repudiare te qui. Natum paulo decore ut nec, ne propriae offendit adipisci has. Eius clita legere mel at, ei vis minimum tincidunt.".to_string());
let big_key_name_ascii = " Lorem ipsum dolor sit amet, ei suas viris sea, deleniti repudiare te qui. Natum paulo decore ut nec, ne propriae offendit adipisci has. Eius clita legere mel at, ei vis minimum tincidunt.".to_string();
let key_triple = KeyTriple::new(
big_app_name_ascii,
ProviderID::CoreProvider,
big_key_name_ascii,
);
let key_id = vec![0x11, 0x22, 0x33];
let _ = manager.insert(key_triple.clone(), key_id.clone()).unwrap();
assert_eq!(manager.remove(&key_triple).unwrap().unwrap(), key_id);
fs::remove_dir_all(path).unwrap();
}
#[test]
fn big_names_emoticons() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/big_names_emoticons_mappings");
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let big_app_name_emoticons = ApplicationName::new("😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮".to_string());
let big_key_name_emoticons = "😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮".to_string();
let key_triple = KeyTriple::new(
big_app_name_emoticons,
ProviderID::MbedProvider,
big_key_name_emoticons,
);
let key_id = vec![0x11, 0x22, 0x33];
let _ = manager.insert(key_triple.clone(), key_id.clone()).unwrap();
assert_eq!(manager.remove(&key_triple).unwrap().unwrap(), key_id);
fs::remove_dir_all(path).unwrap();
}
#[test]
fn create_and_load() {
let path = PathBuf::from(env!("OUT_DIR").to_owned() + "/create_and_load_mappings");
let app_name1 = ApplicationName::new("😀 Application One 😀".to_string());
let key_name1 = "😀 Key One 😀".to_string();
let key_triple1 = KeyTriple::new(app_name1, ProviderID::CoreProvider, key_name1);
let key_id1 = vec![0x11, 0x22, 0x33];
let app_name2 = ApplicationName::new("😇 Application Two 😇".to_string());
let key_name2 = "😇 Key Two 😇".to_string();
let key_triple2 = KeyTriple::new(app_name2, ProviderID::MbedProvider, key_name2);
let key_id2 = vec![0x12, 0x22, 0x32];
let app_name3 = ApplicationName::new("😈 Application Three 😈".to_string());
let key_name3 = "😈 Key Three 😈".to_string();
let key_triple3 = KeyTriple::new(app_name3, ProviderID::CoreProvider, key_name3);
let key_id3 = vec![0x13, 0x23, 0x33];
{
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
let _ = manager
.insert(key_triple1.clone(), key_id1.clone())
.unwrap();
let _ = manager
.insert(key_triple2.clone(), key_id2.clone())
.unwrap();
let _ = manager
.insert(key_triple3.clone(), key_id3.clone())
.unwrap();
}
{
let mut manager = OnDiskKeyIDManager::new(path.clone()).unwrap();
assert_eq!(manager.remove(&key_triple1).unwrap().unwrap(), key_id1);
assert_eq!(manager.remove(&key_triple2).unwrap().unwrap(), key_id2);
assert_eq!(manager.remove(&key_triple3).unwrap().unwrap(), key_id3);
}
fs::remove_dir_all(path).unwrap();
}
fn new_key_triple(key_name: String) -> KeyTriple {
KeyTriple::new(
ApplicationName::new("Testing Application 😎".to_string()),
ProviderID::MbedProvider,
key_name,
)
}
}