mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Keyring storage in bookmarks client (if possible)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
),
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user