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
|
// Local
|
||||||
use crate::bookmarks::serializer::BookmarkSerializer;
|
use crate::bookmarks::serializer::BookmarkSerializer;
|
||||||
use crate::bookmarks::{Bookmark, SerializerError, SerializerErrorKind, UserHosts};
|
use crate::bookmarks::{Bookmark, SerializerError, SerializerErrorKind, UserHosts};
|
||||||
@@ -31,8 +37,7 @@ use crate::utils::crypto;
|
|||||||
use crate::utils::fmt::fmt_time;
|
use crate::utils::fmt::fmt_time;
|
||||||
use crate::utils::random::random_alphanumeric_with_len;
|
use crate::utils::random::random_alphanumeric_with_len;
|
||||||
// Ext
|
// Ext
|
||||||
use std::fs::{OpenOptions, Permissions};
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
@@ -53,23 +58,60 @@ impl BookmarksClient {
|
|||||||
///
|
///
|
||||||
/// Instantiates a new BookmarksClient
|
/// Instantiates a new BookmarksClient
|
||||||
/// Bookmarks file path must be provided
|
/// Bookmarks file path must be provided
|
||||||
/// Key file must be provided
|
/// Storage path for file provider must be provided
|
||||||
pub fn new(
|
pub fn new(
|
||||||
bookmarks_file: &Path,
|
bookmarks_file: &Path,
|
||||||
key_file: &Path,
|
storage_path: &Path,
|
||||||
recents_size: usize,
|
recents_size: usize,
|
||||||
) -> Result<BookmarksClient, SerializerError> {
|
) -> Result<BookmarksClient, SerializerError> {
|
||||||
// Create default hosts
|
// Create default hosts
|
||||||
let default_hosts: UserHosts = Default::default();
|
let default_hosts: UserHosts = Default::default();
|
||||||
// If key file doesn't exist, create key, otherwise read it
|
// Make a key storage (windows / macos)
|
||||||
let key: String = match key_file.exists() {
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||||
true => match BookmarksClient::load_key(key_file) {
|
let (key_storage, service_id): (Box<dyn KeyStorage>, &str) = {
|
||||||
Ok(key) => key,
|
let username: String = whoami::username();
|
||||||
Err(err) => return Err(err),
|
let storage: KeyringStorage = KeyringStorage::new(username.as_str());
|
||||||
},
|
// Check if keyring storage is supported
|
||||||
false => match BookmarksClient::generate_key(key_file) {
|
#[cfg(not(test))]
|
||||||
Ok(key) => key,
|
let app_name: &str = "termscp";
|
||||||
Err(err) => return Err(err),
|
#[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 {
|
let mut client: BookmarksClient = BookmarksClient {
|
||||||
@@ -276,36 +318,10 @@ impl BookmarksClient {
|
|||||||
|
|
||||||
/// ### generate_key
|
/// ### generate_key
|
||||||
///
|
///
|
||||||
/// Generate a new AES key and write it to key file
|
/// Generate a new AES key
|
||||||
fn generate_key(key_file: &Path) -> Result<String, SerializerError> {
|
fn generate_key() -> String {
|
||||||
// Generate 256 bytes (2048 bits) key
|
// Generate 256 bytes (2048 bits) key
|
||||||
let key: String = random_alphanumeric_with_len(256);
|
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(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### make_bookmark
|
/// ### 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_str
|
||||||
///
|
///
|
||||||
/// Encrypt provided string using AES-128. Encrypted buffer is then converted to BASE64
|
/// Encrypt provided string using AES-128. Encrypted buffer is then converted to BASE64
|
||||||
@@ -397,6 +391,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(any(target_os = "unix", target_os = "linux"))]
|
||||||
fn test_system_bookmarks_new_err() {
|
fn test_system_bookmarks_new_err() {
|
||||||
assert!(BookmarksClient::new(
|
assert!(BookmarksClient::new(
|
||||||
Path::new("/tmp/oifoif/omar"),
|
Path::new("/tmp/oifoif/omar"),
|
||||||
@@ -647,9 +642,8 @@ mod tests {
|
|||||||
///
|
///
|
||||||
/// Get paths for configuration and key for bookmarks
|
/// Get paths for configuration and key for bookmarks
|
||||||
fn get_paths(dir: &Path) -> (PathBuf, PathBuf) {
|
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();
|
let mut c: PathBuf = k.clone();
|
||||||
k.push("bookmarks.key");
|
|
||||||
c.push("bookmarks.toml");
|
c.push("bookmarks.toml");
|
||||||
(c, k)
|
(c, k)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,14 +59,12 @@ pub fn init_config_dir() -> Result<Option<PathBuf>, String> {
|
|||||||
/// ### get_bookmarks_paths
|
/// ### get_bookmarks_paths
|
||||||
///
|
///
|
||||||
/// Get paths for bookmarks client
|
/// Get paths for bookmarks client
|
||||||
/// Returns: path of bookmarks.toml and path of key
|
/// Returns: path of bookmarks.toml
|
||||||
pub fn get_bookmarks_paths(config_dir: &Path) -> (PathBuf, PathBuf) {
|
pub fn get_bookmarks_paths(config_dir: &Path) -> PathBuf {
|
||||||
// Prepare paths
|
// Prepare paths
|
||||||
let mut bookmarks_file: PathBuf = PathBuf::from(config_dir);
|
let mut bookmarks_file: PathBuf = PathBuf::from(config_dir);
|
||||||
bookmarks_file.push("bookmarks.toml");
|
bookmarks_file.push("bookmarks.toml");
|
||||||
let mut key_file: PathBuf = PathBuf::from(config_dir);
|
bookmarks_file
|
||||||
key_file.push(".bookmarks.key"); // key file is hidden
|
|
||||||
(bookmarks_file, key_file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### get_config_paths
|
/// ### get_config_paths
|
||||||
@@ -123,10 +121,7 @@ mod tests {
|
|||||||
fn test_system_environment_get_bookmarks_paths() {
|
fn test_system_environment_get_bookmarks_paths() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_bookmarks_paths(&Path::new("/home/omar/.config/termscp/")),
|
get_bookmarks_paths(&Path::new("/home/omar/.config/termscp/")),
|
||||||
(
|
|
||||||
PathBuf::from("/home/omar/.config/termscp/bookmarks.toml"),
|
PathBuf::from("/home/omar/.config/termscp/bookmarks.toml"),
|
||||||
PathBuf::from("/home/omar/.config/termscp/.bookmarks.key")
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ mod tests {
|
|||||||
let username: String = username();
|
let username: String = username();
|
||||||
let storage: KeyringStorage = KeyringStorage::new(username.as_str());
|
let storage: KeyringStorage = KeyringStorage::new(username.as_str());
|
||||||
assert!(storage.is_supported());
|
assert!(storage.is_supported());
|
||||||
let app_name: &str = "termscp";
|
let app_name: &str = "termscp-test2";
|
||||||
let secret: &str = "Th15-15/My-Супер-Секрет";
|
let secret: &str = "Th15-15/My-Супер-Секрет";
|
||||||
let kring: Keyring = Keyring::new(app_name, username.as_str());
|
let kring: Keyring = Keyring::new(app_name, username.as_str());
|
||||||
let _ = kring.delete_password();
|
let _ = kring.delete_password();
|
||||||
|
|||||||
@@ -224,11 +224,15 @@ impl AuthActivity {
|
|||||||
match environment::init_config_dir() {
|
match environment::init_config_dir() {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
// If some configure client, otherwise do nothing; don't bother users telling them that bookmarks are not supported on their system.
|
// 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 {
|
if let Some(config_dir_path) = path {
|
||||||
let (bookmarks_file, key_file): (PathBuf, PathBuf) =
|
let bookmarks_file: PathBuf =
|
||||||
environment::get_bookmarks_paths(path.as_path());
|
environment::get_bookmarks_paths(config_dir_path.as_path());
|
||||||
// Initialize client
|
// 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),
|
Ok(cli) => self.bookmarks_client = Some(cli),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.popup = Some(Popup::Alert(
|
self.popup = Some(Popup::Alert(
|
||||||
@@ -236,7 +240,7 @@ impl AuthActivity {
|
|||||||
format!(
|
format!(
|
||||||
"Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
|
"Could not initialize bookmarks (at \"{}\", \"{}\"): {}",
|
||||||
bookmarks_file.display(),
|
bookmarks_file.display(),
|
||||||
key_file.display(),
|
config_dir_path.display(),
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user