Keyring storage in bookmarks client (if possible)

This commit is contained in:
ChristianVisintin
2021-01-16 16:57:00 +01:00
parent 0192b86422
commit 08d8a3621c
4 changed files with 74 additions and 81 deletions

View File

@@ -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<BookmarksClient, SerializerError> {
// 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<dyn KeyStorage>, &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<dyn KeyStorage>, &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<String, SerializerError> {
/// 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<String, SerializerError> {
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)
}

View File

@@ -59,14 +59,12 @@ pub fn init_config_dir() -> Result<Option<PathBuf>, 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"),
);
}

View File

@@ -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();

View File

@@ -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
),
))