mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
359 lines
13 KiB
Rust
359 lines
13 KiB
Rust
//! ## builder
|
|
//!
|
|
//! Remotefs client builder
|
|
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use remotefs::RemoteFs;
|
|
use remotefs_aws_s3::AwsS3Fs;
|
|
use remotefs_ftp::FtpFs;
|
|
use remotefs_kube::KubeMultiPodFs as KubeFs;
|
|
#[cfg(smb_unix)]
|
|
use remotefs_smb::SmbOptions;
|
|
#[cfg(smb)]
|
|
use remotefs_smb::{SmbCredentials, SmbFs};
|
|
use remotefs_ssh::{ScpFs, SftpFs, SshAgentIdentity, SshConfigParseRule, SshOpts};
|
|
use remotefs_webdav::WebDAVFs;
|
|
|
|
#[cfg(not(smb))]
|
|
use super::params::{AwsS3Params, GenericProtocolParams};
|
|
#[cfg(smb)]
|
|
use super::params::{AwsS3Params, GenericProtocolParams, SmbParams};
|
|
use super::params::{KubeProtocolParams, WebDAVProtocolParams};
|
|
use super::{FileTransferProtocol, ProtocolParams};
|
|
use crate::system::config_client::ConfigClient;
|
|
use crate::system::sshkey_storage::SshKeyStorage;
|
|
use crate::utils::ssh as ssh_utils;
|
|
|
|
/// Remotefs builder
|
|
pub struct RemoteFsBuilder;
|
|
|
|
impl RemoteFsBuilder {
|
|
/// Build RemoteFs client from protocol and params.
|
|
///
|
|
/// if protocol and parameters are inconsistent, the function will panic.
|
|
pub fn build(
|
|
protocol: FileTransferProtocol,
|
|
params: ProtocolParams,
|
|
config_client: &ConfigClient,
|
|
) -> Box<dyn RemoteFs> {
|
|
match (protocol, params) {
|
|
(FileTransferProtocol::AwsS3, ProtocolParams::AwsS3(params)) => {
|
|
Box::new(Self::aws_s3_client(params))
|
|
}
|
|
(FileTransferProtocol::Ftp(secure), ProtocolParams::Generic(params)) => {
|
|
Box::new(Self::ftp_client(params, secure))
|
|
}
|
|
(FileTransferProtocol::Kube, ProtocolParams::Kube(params)) => {
|
|
Box::new(Self::kube_client(params))
|
|
}
|
|
(FileTransferProtocol::Scp, ProtocolParams::Generic(params)) => {
|
|
Box::new(Self::scp_client(params, config_client))
|
|
}
|
|
(FileTransferProtocol::Sftp, ProtocolParams::Generic(params)) => {
|
|
Box::new(Self::sftp_client(params, config_client))
|
|
}
|
|
#[cfg(smb)]
|
|
(FileTransferProtocol::Smb, ProtocolParams::Smb(params)) => {
|
|
Box::new(Self::smb_client(params))
|
|
}
|
|
(FileTransferProtocol::WebDAV, ProtocolParams::WebDAV(params)) => {
|
|
Box::new(Self::webdav_client(params))
|
|
}
|
|
(protocol, params) => {
|
|
error!("Invalid params for protocol '{:?}'", protocol);
|
|
panic!("Invalid protocol '{protocol:?}' with parameters of type {params:?}")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build aws s3 client from parameters
|
|
fn aws_s3_client(params: AwsS3Params) -> AwsS3Fs {
|
|
let mut client = AwsS3Fs::new(params.bucket_name).new_path_style(params.new_path_style);
|
|
if let Some(region) = params.region {
|
|
client = client.region(region);
|
|
}
|
|
if let Some(profile) = params.profile {
|
|
client = client.profile(profile);
|
|
}
|
|
if let Some(endpoint) = params.endpoint {
|
|
client = client.endpoint(endpoint);
|
|
}
|
|
if let Some(access_key) = params.access_key {
|
|
client = client.access_key(access_key);
|
|
}
|
|
if let Some(secret_access_key) = params.secret_access_key {
|
|
client = client.secret_access_key(secret_access_key);
|
|
}
|
|
if let Some(security_token) = params.security_token {
|
|
client = client.security_token(security_token);
|
|
}
|
|
if let Some(session_token) = params.session_token {
|
|
client = client.session_token(session_token);
|
|
}
|
|
client
|
|
}
|
|
|
|
/// Build ftp client from parameters
|
|
fn ftp_client(params: GenericProtocolParams, secure: bool) -> FtpFs {
|
|
let mut client = FtpFs::new(params.address, params.port).passive_mode();
|
|
if let Some(username) = params.username {
|
|
client = client.username(username);
|
|
}
|
|
if let Some(password) = params.password {
|
|
client = client.password(password);
|
|
}
|
|
if secure {
|
|
client = client.secure(true, true);
|
|
}
|
|
client
|
|
}
|
|
|
|
/// Build kube client
|
|
fn kube_client(params: KubeProtocolParams) -> KubeFs {
|
|
let rt = Arc::new(
|
|
tokio::runtime::Builder::new_current_thread()
|
|
.worker_threads(1)
|
|
.enable_all()
|
|
.build()
|
|
.expect("Unable to create tokio runtime"),
|
|
);
|
|
let kube_fs = KubeFs::new(&rt);
|
|
if let Some(config) = params.config() {
|
|
kube_fs.config(config)
|
|
} else {
|
|
kube_fs
|
|
}
|
|
}
|
|
|
|
/// Build scp client
|
|
fn scp_client(params: GenericProtocolParams, config_client: &ConfigClient) -> ScpFs {
|
|
Self::build_ssh_opts(params, config_client).into()
|
|
}
|
|
|
|
/// Build sftp client
|
|
fn sftp_client(params: GenericProtocolParams, config_client: &ConfigClient) -> SftpFs {
|
|
Self::build_ssh_opts(params, config_client).into()
|
|
}
|
|
|
|
#[cfg(smb_unix)]
|
|
fn smb_client(params: SmbParams) -> SmbFs {
|
|
let mut credentials = SmbCredentials::default()
|
|
.server(format!("smb://{}:{}", params.address, params.port))
|
|
.share(params.share);
|
|
|
|
if let Some(username) = params.username {
|
|
credentials = credentials.username(username);
|
|
}
|
|
if let Some(password) = params.password {
|
|
credentials = credentials.password(password);
|
|
}
|
|
if let Some(workgroup) = params.workgroup {
|
|
credentials = credentials.workgroup(workgroup);
|
|
}
|
|
|
|
match SmbFs::try_new(
|
|
credentials,
|
|
SmbOptions::default()
|
|
.one_share_per_server(true)
|
|
.case_sensitive(false),
|
|
) {
|
|
Ok(fs) => fs,
|
|
Err(e) => {
|
|
error!("Invalid params for protocol SMB: {e}");
|
|
panic!("Invalid params for protocol SMB: {e}")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(smb_windows)]
|
|
fn smb_client(params: SmbParams) -> SmbFs {
|
|
let mut credentials = SmbCredentials::new(params.address, params.share);
|
|
|
|
if let Some(username) = params.username {
|
|
credentials = credentials.username(username);
|
|
}
|
|
if let Some(password) = params.password {
|
|
credentials = credentials.password(password);
|
|
}
|
|
|
|
SmbFs::new(credentials)
|
|
}
|
|
|
|
fn webdav_client(params: WebDAVProtocolParams) -> WebDAVFs {
|
|
WebDAVFs::new(¶ms.username, ¶ms.password, ¶ms.uri)
|
|
}
|
|
|
|
/// Build ssh options from generic protocol params and client configuration
|
|
fn build_ssh_opts(params: GenericProtocolParams, config_client: &ConfigClient) -> SshOpts {
|
|
let mut opts = SshOpts::new(params.address.clone())
|
|
.key_storage(Box::new(Self::make_ssh_storage(config_client)))
|
|
.ssh_agent_identity(Some(SshAgentIdentity::All))
|
|
.port(params.port);
|
|
// get ssh config
|
|
let ssh_config = config_client
|
|
.get_ssh_config()
|
|
.and_then(|path| {
|
|
debug!("reading ssh config at {}", path);
|
|
ssh_utils::parse_ssh2_config(path).ok()
|
|
})
|
|
.map(|config| config.query(¶ms.address));
|
|
|
|
//* override port
|
|
if let Some(port) = ssh_config.as_ref().and_then(|config| config.port) {
|
|
opts = opts.port(port);
|
|
}
|
|
|
|
//* get username. Case 1 provided in params
|
|
if let Some(username) = params.username {
|
|
opts = opts.username(username);
|
|
} else if let Some(ssh_config) = &ssh_config {
|
|
debug!("no username was provided, checking whether a user is set for this host");
|
|
if let Some(username) = &ssh_config.user {
|
|
debug!("found username from config: {username}");
|
|
opts = opts.username(username);
|
|
} else {
|
|
//* case 3: use system username; can't be None
|
|
debug!("no username was provided, using current username");
|
|
opts = opts.username(whoami::username());
|
|
}
|
|
} else {
|
|
//* case 3: use system username; can't be None
|
|
debug!("no username was provided, using current username");
|
|
opts = opts.username(whoami::username());
|
|
}
|
|
if let Some(password) = params.password {
|
|
opts = opts.password(password);
|
|
}
|
|
if let Some(config_path) = config_client.get_ssh_config() {
|
|
opts = opts.config_file(
|
|
PathBuf::from(config_path),
|
|
SshConfigParseRule::ALLOW_UNKNOWN_FIELDS,
|
|
);
|
|
}
|
|
opts
|
|
}
|
|
|
|
/// Make ssh storage from `ConfigClient` if possible, empty otherwise (empty is implicit if degraded)
|
|
fn make_ssh_storage(config_client: &ConfigClient) -> SshKeyStorage {
|
|
SshKeyStorage::from(config_client)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use tempfile::TempDir;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn should_build_aws_s3_fs() {
|
|
let params = ProtocolParams::AwsS3(
|
|
AwsS3Params::new("omar", Some("eu-west-1"), Some("test"))
|
|
.endpoint(Some("http://localhost:9000"))
|
|
.new_path_style(true)
|
|
.access_key(Some("pippo"))
|
|
.secret_access_key(Some("pluto"))
|
|
.security_token(Some("omar"))
|
|
.session_token(Some("gerry-scotti")),
|
|
);
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::AwsS3, params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
fn should_build_ftp_fs() {
|
|
let params = ProtocolParams::Generic(
|
|
GenericProtocolParams::default()
|
|
.address("127.0.0.1")
|
|
.port(21)
|
|
.username(Some("omar"))
|
|
.password(Some("qwerty123")),
|
|
);
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::Ftp(true), params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
fn test_should_build_kube_fs() {
|
|
let params = ProtocolParams::Kube(KubeProtocolParams {
|
|
namespace: Some("namespace".to_string()),
|
|
cluster_url: Some("cluster_url".to_string()),
|
|
username: Some("username".to_string()),
|
|
client_cert: Some("client_cert".to_string()),
|
|
client_key: Some("client_key".to_string()),
|
|
});
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::Kube, params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
fn should_build_scp_fs() {
|
|
let params = ProtocolParams::Generic(
|
|
GenericProtocolParams::default()
|
|
.address("127.0.0.1")
|
|
.port(22)
|
|
.username(Some("omar"))
|
|
.password(Some("qwerty123")),
|
|
);
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::Scp, params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
fn should_build_sftp_fs() {
|
|
let params = ProtocolParams::Generic(
|
|
GenericProtocolParams::default()
|
|
.address("127.0.0.1")
|
|
.port(22)
|
|
.username(Some("omar"))
|
|
.password(Some("qwerty123")),
|
|
);
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::Sftp, params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(smb)]
|
|
fn should_build_smb_fs() {
|
|
let params = ProtocolParams::Smb(SmbParams::new("localhost", "share"));
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::Smb, params, &config_client);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn should_not_build_fs() {
|
|
let params = ProtocolParams::Generic(
|
|
GenericProtocolParams::default()
|
|
.address("127.0.0.1")
|
|
.port(22)
|
|
.username(Some("omar"))
|
|
.password(Some("qwerty123")),
|
|
);
|
|
let config_client = get_config_client();
|
|
let _ = RemoteFsBuilder::build(FileTransferProtocol::AwsS3, params, &config_client);
|
|
}
|
|
|
|
fn get_config_client() -> ConfigClient {
|
|
let tmp_dir: TempDir = TempDir::new().ok().unwrap();
|
|
let (cfg_path, ssh_keys_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
|
|
ConfigClient::new(cfg_path.as_path(), ssh_keys_path.as_path())
|
|
.ok()
|
|
.unwrap()
|
|
}
|
|
|
|
/// 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)
|
|
}
|
|
}
|