From 08d8a3621c36bf305ad5eeee0a0d3b4a725156f8 Mon Sep 17 00:00:00 2001 From: ChristianVisintin Date: Sat, 16 Jan 2021 16:57:00 +0100 Subject: [PATCH] Keyring storage in bookmarks client (if possible) --- src/system/bookmarks_client.rs | 126 +++++++++---------- src/system/environment.rs | 13 +- src/system/keys/keyringstorage.rs | 2 +- src/ui/activities/auth_activity/bookmarks.rs | 14 ++- 4 files changed, 74 insertions(+), 81 deletions(-) diff --git a/src/system/bookmarks_client.rs b/src/system/bookmarks_client.rs index 3c9510f..cec7d77 100644 --- a/src/system/bookmarks_client.rs +++ b/src/system/bookmarks_client.rs @@ -23,6 +23,12 @@ * */ +// Deps +extern crate whoami; +// Crate +#[cfg(any(target_os = "windows", target_os = "macos"))] +use super::keys::keyringstorage::KeyringStorage; +use super::keys::{filestorage::FileStorage, KeyStorage, KeyStorageError}; // Local use crate::bookmarks::serializer::BookmarkSerializer; use crate::bookmarks::{Bookmark, SerializerError, SerializerErrorKind, UserHosts}; @@ -31,8 +37,7 @@ use crate::utils::crypto; use crate::utils::fmt::fmt_time; use crate::utils::random::random_alphanumeric_with_len; // Ext -use std::fs::{OpenOptions, Permissions}; -use std::io::{Read, Write}; +use std::fs::OpenOptions; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::string::ToString; @@ -53,23 +58,60 @@ impl BookmarksClient { /// /// Instantiates a new BookmarksClient /// Bookmarks file path must be provided - /// Key file must be provided + /// Storage path for file provider must be provided pub fn new( bookmarks_file: &Path, - key_file: &Path, + storage_path: &Path, recents_size: usize, ) -> Result { // Create default hosts let default_hosts: UserHosts = Default::default(); - // If key file doesn't exist, create key, otherwise read it - let key: String = match key_file.exists() { - true => match BookmarksClient::load_key(key_file) { - Ok(key) => key, - Err(err) => return Err(err), - }, - false => match BookmarksClient::generate_key(key_file) { - Ok(key) => key, - Err(err) => return Err(err), + // Make a key storage (windows / macos) + #[cfg(any(target_os = "windows", target_os = "macos"))] + let (key_storage, service_id): (Box, &str) = { + let username: String = whoami::username(); + let storage: KeyringStorage = KeyringStorage::new(username.as_str()); + // Check if keyring storage is supported + #[cfg(not(test))] + let app_name: &str = "termscp"; + #[cfg(test)] // NOTE: when running test, add -test + let app_name: &str = "termscp-test"; + match storage.is_supported() { + true => (Box::new(storage), app_name), + false => (Box::new(FileStorage::new(storage_path)), "bookmarks"), + } + }; + // Make a key storage (linux / unix) + #[cfg(any(target_os = "linux", target_os = "unix"))] + let (key_storage, service_id): (Box, &str) = { + #[cfg(not(test))] + let app_name: &str = "bookmarks"; + #[cfg(test)] // NOTE: when running test, add -test + let app_name: &str = "bookmarks-test"; + (Box::new(FileStorage::new(storage_path)), app_name) + }; + // Load key + let key: String = match key_storage.get_key(service_id) { + Ok(k) => k, + Err(e) => match e { + KeyStorageError::NoSuchKey => { + // If no such key, generate key and set it into the storage + let key: String = Self::generate_key(); + if let Err(e) = key_storage.set_key(service_id, key.as_str()) { + return Err(SerializerError::new_ex( + SerializerErrorKind::IoError, + format!("Could not write key to storage: {}", e), + )); + } + // Return key + key + } + _ => { + return Err(SerializerError::new_ex( + SerializerErrorKind::IoError, + format!("Could not get key from storage: {}", e), + )) + } }, }; let mut client: BookmarksClient = BookmarksClient { @@ -276,36 +318,10 @@ impl BookmarksClient { /// ### generate_key /// - /// Generate a new AES key and write it to key file - fn generate_key(key_file: &Path) -> Result { + /// Generate a new AES key + fn generate_key() -> String { // Generate 256 bytes (2048 bits) key - let key: String = random_alphanumeric_with_len(256); - // Write file - match OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(key_file) - { - Ok(mut file) => { - // Write key to file - if let Err(err) = file.write_all(key.as_bytes()) { - return Err(SerializerError::new_ex( - SerializerErrorKind::IoError, - err.to_string(), - )); - } - // Set file to readonly - let mut permissions: Permissions = file.metadata().unwrap().permissions(); - permissions.set_readonly(true); - let _ = file.set_permissions(permissions); - Ok(key) - } - Err(err) => Err(SerializerError::new_ex( - SerializerErrorKind::IoError, - err.to_string(), - )), - } + random_alphanumeric_with_len(256) } /// ### make_bookmark @@ -331,28 +347,6 @@ impl BookmarksClient { } } - /// ### load_key - /// - /// Load key from key_file - fn load_key(key_file: &Path) -> Result { - match OpenOptions::new().read(true).open(key_file) { - Ok(mut file) => { - let mut key: String = String::with_capacity(256); - match file.read_to_string(&mut key) { - Ok(_) => Ok(key), - Err(err) => Err(SerializerError::new_ex( - SerializerErrorKind::IoError, - err.to_string(), - )), - } - } - Err(err) => Err(SerializerError::new_ex( - SerializerErrorKind::IoError, - err.to_string(), - )), - } - } - /// ### encrypt_str /// /// Encrypt provided string using AES-128. Encrypted buffer is then converted to BASE64 @@ -397,6 +391,7 @@ mod tests { } #[test] + #[cfg(any(target_os = "unix", target_os = "linux"))] fn test_system_bookmarks_new_err() { assert!(BookmarksClient::new( Path::new("/tmp/oifoif/omar"), @@ -647,9 +642,8 @@ mod tests { /// /// Get paths for configuration and key for bookmarks fn get_paths(dir: &Path) -> (PathBuf, PathBuf) { - let mut k: PathBuf = PathBuf::from(dir); + let k: PathBuf = PathBuf::from(dir); let mut c: PathBuf = k.clone(); - k.push("bookmarks.key"); c.push("bookmarks.toml"); (c, k) } diff --git a/src/system/environment.rs b/src/system/environment.rs index 0b994ae..b9c7973 100644 --- a/src/system/environment.rs +++ b/src/system/environment.rs @@ -59,14 +59,12 @@ pub fn init_config_dir() -> Result, String> { /// ### get_bookmarks_paths /// /// Get paths for bookmarks client -/// Returns: path of bookmarks.toml and path of key -pub fn get_bookmarks_paths(config_dir: &Path) -> (PathBuf, PathBuf) { +/// Returns: path of bookmarks.toml +pub fn get_bookmarks_paths(config_dir: &Path) -> PathBuf { // Prepare paths let mut bookmarks_file: PathBuf = PathBuf::from(config_dir); bookmarks_file.push("bookmarks.toml"); - let mut key_file: PathBuf = PathBuf::from(config_dir); - key_file.push(".bookmarks.key"); // key file is hidden - (bookmarks_file, key_file) + bookmarks_file } /// ### get_config_paths @@ -123,10 +121,7 @@ mod tests { fn test_system_environment_get_bookmarks_paths() { assert_eq!( get_bookmarks_paths(&Path::new("/home/omar/.config/termscp/")), - ( - PathBuf::from("/home/omar/.config/termscp/bookmarks.toml"), - PathBuf::from("/home/omar/.config/termscp/.bookmarks.key") - ) + PathBuf::from("/home/omar/.config/termscp/bookmarks.toml"), ); } diff --git a/src/system/keys/keyringstorage.rs b/src/system/keys/keyringstorage.rs index 9eb29ab..93f85c0 100644 --- a/src/system/keys/keyringstorage.rs +++ b/src/system/keys/keyringstorage.rs @@ -114,7 +114,7 @@ mod tests { let username: String = username(); let storage: KeyringStorage = KeyringStorage::new(username.as_str()); assert!(storage.is_supported()); - let app_name: &str = "termscp"; + let app_name: &str = "termscp-test2"; let secret: &str = "Th15-15/My-Супер-Секрет"; let kring: Keyring = Keyring::new(app_name, username.as_str()); let _ = kring.delete_password(); diff --git a/src/ui/activities/auth_activity/bookmarks.rs b/src/ui/activities/auth_activity/bookmarks.rs index f9f553d..0e2f99c 100644 --- a/src/ui/activities/auth_activity/bookmarks.rs +++ b/src/ui/activities/auth_activity/bookmarks.rs @@ -224,11 +224,15 @@ impl AuthActivity { match environment::init_config_dir() { Ok(path) => { // If some configure client, otherwise do nothing; don't bother users telling them that bookmarks are not supported on their system. - if let Some(path) = path { - let (bookmarks_file, key_file): (PathBuf, PathBuf) = - environment::get_bookmarks_paths(path.as_path()); + if let Some(config_dir_path) = path { + let bookmarks_file: PathBuf = + environment::get_bookmarks_paths(config_dir_path.as_path()); // Initialize client - match BookmarksClient::new(bookmarks_file.as_path(), key_file.as_path(), 16) { + match BookmarksClient::new( + bookmarks_file.as_path(), + config_dir_path.as_path(), + 16, + ) { Ok(cli) => self.bookmarks_client = Some(cli), Err(err) => { self.popup = Some(Popup::Alert( @@ -236,7 +240,7 @@ impl AuthActivity { format!( "Could not initialize bookmarks (at \"{}\", \"{}\"): {}", bookmarks_file.display(), - key_file.display(), + config_dir_path.display(), err ), ))