648 lines
23 KiB
Rust
648 lines
23 KiB
Rust
//! ## ConfigClient
|
|
//!
|
|
//! `config_client` is the module which provides an API between the Config module and the system
|
|
|
|
/**
|
|
* MIT License
|
|
*
|
|
* termscp - Copyright (c) 2021 Christian Visintin
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
// Deps
|
|
extern crate rand;
|
|
// Locals
|
|
use crate::config::serializer::ConfigSerializer;
|
|
use crate::config::{SerializerError, SerializerErrorKind, UserConfig};
|
|
use crate::filetransfer::FileTransferProtocol;
|
|
use crate::fs::explorer::GroupDirs;
|
|
// Ext
|
|
use std::fs::{create_dir, remove_file, File, OpenOptions};
|
|
use std::io::Write;
|
|
use std::path::{Path, PathBuf};
|
|
use std::str::FromStr;
|
|
use std::string::ToString;
|
|
|
|
// Types
|
|
pub type SshHost = (String, String, PathBuf); // 0: host, 1: username, 2: RSA key path
|
|
|
|
/// ## ConfigClient
|
|
///
|
|
/// ConfigClient provides a high level API to communicate with the termscp configuration
|
|
pub struct ConfigClient {
|
|
config: UserConfig, // Configuration loaded
|
|
config_path: PathBuf, // Configuration TOML Path
|
|
ssh_key_dir: PathBuf, // SSH Key storage directory
|
|
}
|
|
|
|
impl ConfigClient {
|
|
/// ### new
|
|
///
|
|
/// Instantiate a new `ConfigClient` with provided path
|
|
pub fn new(config_path: &Path, ssh_key_dir: &Path) -> Result<ConfigClient, SerializerError> {
|
|
// Initialize a default configuration
|
|
let default_config: UserConfig = UserConfig::default();
|
|
info!(
|
|
"Setting up config client with config path {} and SSH key directory {}",
|
|
config_path.display(),
|
|
ssh_key_dir.display()
|
|
);
|
|
// Create client
|
|
let mut client: ConfigClient = ConfigClient {
|
|
config: default_config,
|
|
config_path: PathBuf::from(config_path),
|
|
ssh_key_dir: PathBuf::from(ssh_key_dir),
|
|
};
|
|
// If ssh key directory doesn't exist, create it
|
|
if !ssh_key_dir.exists() {
|
|
if let Err(err) = create_dir(ssh_key_dir) {
|
|
error!("Failed to create SSH key dir: {}", err);
|
|
return Err(SerializerError::new_ex(
|
|
SerializerErrorKind::IoError,
|
|
format!(
|
|
"Could not create SSH key directory \"{}\": {}",
|
|
ssh_key_dir.display(),
|
|
err
|
|
),
|
|
));
|
|
}
|
|
debug!("Created SSH key directory");
|
|
}
|
|
// If Config file doesn't exist, create it
|
|
if !config_path.exists() {
|
|
if let Err(err) = client.write_config() {
|
|
error!("Couldn't create configuration file: {}", err);
|
|
return Err(err);
|
|
}
|
|
debug!("Config file didn't exist; created file");
|
|
} else {
|
|
// otherwise Load configuration from file
|
|
if let Err(err) = client.read_config() {
|
|
error!("Couldn't read configuration file: {}", err);
|
|
return Err(err);
|
|
}
|
|
debug!("Read configuration file");
|
|
}
|
|
Ok(client)
|
|
}
|
|
|
|
// Text editor
|
|
|
|
/// ### get_text_editor
|
|
///
|
|
/// Get text editor from configuration
|
|
pub fn get_text_editor(&self) -> PathBuf {
|
|
self.config.user_interface.text_editor.clone()
|
|
}
|
|
|
|
/// ### set_text_editor
|
|
///
|
|
/// Set text editor path
|
|
pub fn set_text_editor(&mut self, path: PathBuf) {
|
|
self.config.user_interface.text_editor = path;
|
|
}
|
|
|
|
// Default protocol
|
|
|
|
/// ### get_default_protocol
|
|
///
|
|
/// Get default protocol from configuration
|
|
pub fn get_default_protocol(&self) -> FileTransferProtocol {
|
|
match FileTransferProtocol::from_str(self.config.user_interface.default_protocol.as_str()) {
|
|
Ok(p) => p,
|
|
Err(_) => FileTransferProtocol::Sftp,
|
|
}
|
|
}
|
|
|
|
/// ### set_default_protocol
|
|
///
|
|
/// Set default protocol to configuration
|
|
pub fn set_default_protocol(&mut self, proto: FileTransferProtocol) {
|
|
self.config.user_interface.default_protocol = proto.to_string();
|
|
}
|
|
|
|
/// ### get_show_hidden_files
|
|
///
|
|
/// Get value of `show_hidden_files`
|
|
pub fn get_show_hidden_files(&self) -> bool {
|
|
self.config.user_interface.show_hidden_files
|
|
}
|
|
|
|
/// ### set_show_hidden_files
|
|
///
|
|
/// Set new value for `show_hidden_files`
|
|
pub fn set_show_hidden_files(&mut self, value: bool) {
|
|
self.config.user_interface.show_hidden_files = value;
|
|
}
|
|
|
|
/// ### get_check_for_updates
|
|
///
|
|
/// Get value of `check_for_updates`
|
|
pub fn get_check_for_updates(&self) -> bool {
|
|
self.config.user_interface.check_for_updates.unwrap_or(true)
|
|
}
|
|
|
|
/// ### set_check_for_updates
|
|
///
|
|
/// Set new value for `check_for_updates`
|
|
pub fn set_check_for_updates(&mut self, value: bool) {
|
|
self.config.user_interface.check_for_updates = Some(value);
|
|
}
|
|
|
|
/// ### get_group_dirs
|
|
///
|
|
/// Get GroupDirs value from configuration (will be converted from string)
|
|
pub fn get_group_dirs(&self) -> Option<GroupDirs> {
|
|
// Convert string to `GroupDirs`
|
|
match &self.config.user_interface.group_dirs {
|
|
None => None,
|
|
Some(val) => match GroupDirs::from_str(val.as_str()) {
|
|
Ok(val) => Some(val),
|
|
Err(_) => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
/// ### set_group_dirs
|
|
///
|
|
/// Set value for group_dir in configuration.
|
|
/// Provided value, if `Some` will be converted to `GroupDirs`
|
|
pub fn set_group_dirs(&mut self, val: Option<GroupDirs>) {
|
|
self.config.user_interface.group_dirs = val.map(|val| val.to_string());
|
|
}
|
|
|
|
/// ### get_local_file_fmt
|
|
///
|
|
/// Get current file fmt for local host
|
|
pub fn get_local_file_fmt(&self) -> Option<String> {
|
|
self.config.user_interface.file_fmt.clone()
|
|
}
|
|
|
|
/// ### set_local_file_fmt
|
|
///
|
|
/// Set file fmt parameter for local host
|
|
pub fn set_local_file_fmt(&mut self, s: String) {
|
|
self.config.user_interface.file_fmt = match s.is_empty() {
|
|
true => None,
|
|
false => Some(s),
|
|
};
|
|
}
|
|
|
|
/// ### get_remote_file_fmt
|
|
///
|
|
/// Get current file fmt for remote host
|
|
pub fn get_remote_file_fmt(&self) -> Option<String> {
|
|
self.config.user_interface.remote_file_fmt.clone()
|
|
}
|
|
|
|
/// ### set_remote_file_fmt
|
|
///
|
|
/// Set file fmt parameter for remote host
|
|
pub fn set_remote_file_fmt(&mut self, s: String) {
|
|
self.config.user_interface.remote_file_fmt = match s.is_empty() {
|
|
true => None,
|
|
false => Some(s),
|
|
};
|
|
}
|
|
|
|
// SSH Keys
|
|
|
|
/// ### save_ssh_key
|
|
///
|
|
/// Save a SSH key into configuration.
|
|
/// This operation also creates the key file in `ssh_key_dir`
|
|
/// and also commits changes to configuration, to prevent incoerent data
|
|
pub fn add_ssh_key(
|
|
&mut self,
|
|
host: &str,
|
|
username: &str,
|
|
ssh_key: &str,
|
|
) -> Result<(), SerializerError> {
|
|
let host_name: String = Self::make_ssh_host_key(host, username);
|
|
// Get key path
|
|
let ssh_key_path: PathBuf = {
|
|
let mut p: PathBuf = self.ssh_key_dir.clone();
|
|
p.push(format!("{}.key", host_name));
|
|
p
|
|
};
|
|
info!(
|
|
"Writing SSH file to {} for host {}",
|
|
ssh_key_path.display(),
|
|
host_name
|
|
);
|
|
// Write key to file
|
|
let mut f: File = match File::create(ssh_key_path.as_path()) {
|
|
Ok(f) => f,
|
|
Err(err) => return Self::make_io_err(err),
|
|
};
|
|
if let Err(err) = f.write_all(ssh_key.as_bytes()) {
|
|
error!("Failed to write SSH key to file: {}", err);
|
|
return Self::make_io_err(err);
|
|
}
|
|
// Add host to keys
|
|
self.config.remote.ssh_keys.insert(host_name, ssh_key_path);
|
|
// Write config
|
|
self.write_config()
|
|
}
|
|
|
|
/// ### del_ssh_key
|
|
///
|
|
/// Delete a ssh key from configuration, using host as key.
|
|
/// This operation also unlinks the key file in `ssh_key_dir`
|
|
/// and also commits changes to configuration, to prevent incoerent data
|
|
pub fn del_ssh_key(&mut self, host: &str, username: &str) -> Result<(), SerializerError> {
|
|
// Remove key from configuration and get key path
|
|
info!("Removing key for {}@{}", host, username);
|
|
let key_path: PathBuf = match self
|
|
.config
|
|
.remote
|
|
.ssh_keys
|
|
.remove(&Self::make_ssh_host_key(host, username))
|
|
{
|
|
Some(p) => p,
|
|
None => return Ok(()), // Return ok if host doesn't exist
|
|
};
|
|
// Remove file
|
|
if let Err(err) = remove_file(key_path.as_path()) {
|
|
error!("Failed to remove key file {}: {}", key_path.display(), err);
|
|
return Self::make_io_err(err);
|
|
}
|
|
// Commit changes to configuration
|
|
self.write_config()
|
|
}
|
|
|
|
/// ### get_ssh_key
|
|
///
|
|
/// Get ssh key from host.
|
|
/// None is returned if key doesn't exist
|
|
/// `std::io::Error` is returned in case it was not possible to read the key file
|
|
pub fn get_ssh_key(&self, mkey: &str) -> std::io::Result<Option<SshHost>> {
|
|
// Check if Key exists
|
|
match self.config.remote.ssh_keys.get(mkey) {
|
|
None => Ok(None),
|
|
Some(key_path) => {
|
|
// Get host and username
|
|
let (host, username): (String, String) = Self::get_ssh_tokens(mkey);
|
|
// Return key
|
|
Ok(Some((host, username, PathBuf::from(key_path))))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// ### iter_ssh_keys
|
|
///
|
|
/// Get an iterator through hosts in the ssh key storage
|
|
pub fn iter_ssh_keys(&self) -> impl Iterator<Item = &String> + '_ {
|
|
Box::new(self.config.remote.ssh_keys.keys())
|
|
}
|
|
|
|
// I/O
|
|
|
|
/// ### write_config
|
|
///
|
|
/// Write configuration to file
|
|
pub fn write_config(&self) -> Result<(), SerializerError> {
|
|
// Open file
|
|
match OpenOptions::new()
|
|
.create(true)
|
|
.write(true)
|
|
.truncate(true)
|
|
.open(self.config_path.as_path())
|
|
{
|
|
Ok(writer) => {
|
|
let serializer: ConfigSerializer = ConfigSerializer {};
|
|
serializer.serialize(Box::new(writer), &self.config)
|
|
}
|
|
Err(err) => {
|
|
error!("Failed to write configuration file: {}", err);
|
|
Err(SerializerError::new_ex(
|
|
SerializerErrorKind::IoError,
|
|
err.to_string(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// ### read_config
|
|
///
|
|
/// Read configuration from file (or reload it if already read)
|
|
pub fn read_config(&mut self) -> Result<(), SerializerError> {
|
|
// Open bookmarks file for read
|
|
match OpenOptions::new()
|
|
.read(true)
|
|
.open(self.config_path.as_path())
|
|
{
|
|
Ok(reader) => {
|
|
// Deserialize
|
|
let deserializer: ConfigSerializer = ConfigSerializer {};
|
|
match deserializer.deserialize(Box::new(reader)) {
|
|
Ok(config) => {
|
|
self.config = config;
|
|
Ok(())
|
|
}
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
Err(err) => {
|
|
error!("Failed to read configuration: {}", err);
|
|
Err(SerializerError::new_ex(
|
|
SerializerErrorKind::IoError,
|
|
err.to_string(),
|
|
))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// ### make_ssh_host_key
|
|
///
|
|
/// Hosts are saved as `username@host` into configuration.
|
|
/// This method creates the key name, starting from host and username
|
|
fn make_ssh_host_key(host: &str, username: &str) -> String {
|
|
format!("{}@{}", username, host)
|
|
}
|
|
|
|
/// ### get_ssh_tokens
|
|
///
|
|
/// Get ssh tokens starting from ssh host key
|
|
/// Panics if key has invalid syntax
|
|
/// Returns: (host, username)
|
|
fn get_ssh_tokens(host_key: &str) -> (String, String) {
|
|
let tokens: Vec<&str> = host_key.split('@').collect();
|
|
assert_eq!(tokens.len(), 2);
|
|
(String::from(tokens[1]), String::from(tokens[0]))
|
|
}
|
|
|
|
/// ### make_io_err
|
|
///
|
|
/// Make serializer error from `std::io::Error`
|
|
fn make_io_err(err: std::io::Error) -> Result<(), SerializerError> {
|
|
Err(SerializerError::new_ex(
|
|
SerializerErrorKind::IoError,
|
|
err.to_string(),
|
|
))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
use super::*;
|
|
use crate::config::UserConfig;
|
|
use crate::utils::random::random_alphanumeric_with_len;
|
|
|
|
use pretty_assertions::assert_eq;
|
|
use std::io::Read;
|
|
use tempfile::TempDir;
|
|
|
|
#[test]
|
|
fn test_system_config_new() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, ssh_keys_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let client: ConfigClient = ConfigClient::new(cfg_path.as_path(), ssh_keys_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
// Verify parameters
|
|
let default_config: UserConfig = UserConfig::default();
|
|
assert_eq!(client.config.remote.ssh_keys.len(), 0);
|
|
assert_eq!(
|
|
client.config.user_interface.default_protocol,
|
|
default_config.user_interface.default_protocol
|
|
);
|
|
assert_eq!(
|
|
client.config.user_interface.text_editor,
|
|
default_config.user_interface.text_editor
|
|
);
|
|
assert_eq!(client.config_path, cfg_path);
|
|
assert_eq!(client.ssh_key_dir, ssh_keys_path);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_new_err() {
|
|
assert!(
|
|
ConfigClient::new(Path::new("/tmp/oifoif/omar"), Path::new("/tmp/efnnu/omar"),)
|
|
.is_err()
|
|
);
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
assert!(ConfigClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar")).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_from_existing() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
// Change some stuff
|
|
client.set_text_editor(PathBuf::from("/usr/bin/vim"));
|
|
client.set_default_protocol(FileTransferProtocol::Scp);
|
|
assert!(client
|
|
.add_ssh_key("192.168.1.31", "pi", "piroporopero")
|
|
.is_ok());
|
|
assert!(client.write_config().is_ok());
|
|
// Istantiate a new client
|
|
let client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
// Verify client has updated parameters
|
|
assert_eq!(client.get_default_protocol(), FileTransferProtocol::Scp);
|
|
assert_eq!(client.get_text_editor(), PathBuf::from("/usr/bin/vim"));
|
|
let mut expected_key_path: PathBuf = key_path.clone();
|
|
expected_key_path.push("pi@192.168.1.31.key");
|
|
assert_eq!(
|
|
client.get_ssh_key("pi@192.168.1.31").unwrap().unwrap(),
|
|
(
|
|
String::from("192.168.1.31"),
|
|
String::from("pi"),
|
|
expected_key_path,
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_text_editor() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
client.set_text_editor(PathBuf::from("mcedit"));
|
|
assert_eq!(client.get_text_editor(), PathBuf::from("mcedit"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_default_protocol() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
client.set_default_protocol(FileTransferProtocol::Ftp(true));
|
|
assert_eq!(
|
|
client.get_default_protocol(),
|
|
FileTransferProtocol::Ftp(true)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_show_hidden_files() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
client.set_show_hidden_files(true);
|
|
assert_eq!(client.get_show_hidden_files(), true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_check_for_updates() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
assert_eq!(client.get_check_for_updates(), true); // Null ?
|
|
client.set_check_for_updates(true);
|
|
assert_eq!(client.get_check_for_updates(), true);
|
|
client.set_check_for_updates(false);
|
|
assert_eq!(client.get_check_for_updates(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_group_dirs() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
client.set_group_dirs(Some(GroupDirs::First));
|
|
assert_eq!(client.get_group_dirs(), Some(GroupDirs::First),);
|
|
client.set_group_dirs(None);
|
|
assert_eq!(client.get_group_dirs(), None,);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_local_file_fmt() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
assert_eq!(client.get_local_file_fmt(), None);
|
|
client.set_local_file_fmt(String::from("{NAME}"));
|
|
assert_eq!(client.get_local_file_fmt().unwrap(), String::from("{NAME}"));
|
|
// Delete
|
|
client.set_local_file_fmt(String::from(""));
|
|
assert_eq!(client.get_local_file_fmt(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_remote_file_fmt() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
assert_eq!(client.get_remote_file_fmt(), None);
|
|
client.set_remote_file_fmt(String::from("{NAME}"));
|
|
assert_eq!(
|
|
client.get_remote_file_fmt().unwrap(),
|
|
String::from("{NAME}")
|
|
);
|
|
// Delete
|
|
client.set_remote_file_fmt(String::from(""));
|
|
assert_eq!(client.get_remote_file_fmt(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_ssh_keys() {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
|
|
.ok()
|
|
.unwrap();
|
|
// Add a new key
|
|
let rsa_key: String = get_sample_rsa_key();
|
|
assert!(client
|
|
.add_ssh_key("192.168.1.31", "pi", rsa_key.as_str())
|
|
.is_ok());
|
|
// Iterate keys
|
|
for key in client.iter_ssh_keys() {
|
|
let host: SshHost = client.get_ssh_key(key).ok().unwrap().unwrap();
|
|
assert_eq!(host.0, String::from("192.168.1.31"));
|
|
assert_eq!(host.1, String::from("pi"));
|
|
let mut expected_key_path: PathBuf = key_path.clone();
|
|
expected_key_path.push("pi@192.168.1.31.key");
|
|
assert_eq!(host.2, expected_key_path);
|
|
// Read rsa key
|
|
let mut key_file: File = File::open(expected_key_path.as_path()).ok().unwrap();
|
|
// Read
|
|
let mut key: String = String::new();
|
|
assert!(key_file.read_to_string(&mut key).is_ok());
|
|
// Verify rsa key
|
|
assert_eq!(key, rsa_key);
|
|
}
|
|
// Unexisting key
|
|
assert!(client.get_ssh_key("test").ok().unwrap().is_none());
|
|
// Delete key
|
|
assert!(client.del_ssh_key("192.168.1.31", "pi").is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_make_key() {
|
|
assert_eq!(
|
|
ConfigClient::make_ssh_host_key("192.168.1.31", "pi"),
|
|
String::from("pi@192.168.1.31")
|
|
);
|
|
assert_eq!(
|
|
ConfigClient::get_ssh_tokens("pi@192.168.1.31"),
|
|
(String::from("192.168.1.31"), String::from("pi"))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_system_config_make_io_err() {
|
|
let err: SerializerError =
|
|
ConfigClient::make_io_err(std::io::Error::from(std::io::ErrorKind::PermissionDenied))
|
|
.err()
|
|
.unwrap();
|
|
assert_eq!(err.to_string(), "IO error (permission denied)");
|
|
}
|
|
|
|
/// ### get_paths
|
|
///
|
|
/// Get paths for configuration and keys directory
|
|
fn get_paths(dir: &Path) -> (PathBuf, PathBuf) {
|
|
let mut k: PathBuf = PathBuf::from(dir);
|
|
let mut c: PathBuf = k.clone();
|
|
k.push("ssh-keys/");
|
|
c.push("config.toml");
|
|
(c, k)
|
|
}
|
|
|
|
fn get_sample_rsa_key() -> String {
|
|
format!(
|
|
"-----BEGIN OPENSSH PRIVATE KEY-----\n{}\n-----END OPENSSH PRIVATE KEY-----",
|
|
random_alphanumeric_with_len(2536)
|
|
)
|
|
}
|
|
}
|