pub mod localhost;
pub mod ssh2;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use crate::error::RegentError;
use crate::hosts::handlers::localhost::WhichUser;
use crate::hosts::handlers::ssh2::Ssh2Auth;
use crate::secrets::SecretProvider;
use crate::secrets::SecretReference;
use crate::{LocalHostHandler, Ssh2HostHandler};
use crate::{command::CommandResult, hosts::privilege::Privilege};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TargetUserKind {
CurrentUser,
User(SecretReference),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "PascalCase")]
pub struct TargetUser {
pub user_kind: TargetUserKind,
}
impl TargetUser {
pub fn current_user() -> Self {
Self {
user_kind: TargetUserKind::CurrentUser,
}
}
pub fn user(sec_ref: &str, provider: Option<String>) -> Self {
Self {
user_kind: TargetUserKind::User(SecretReference::from(sec_ref, provider)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum ConnectionMethod {
Localhost(TargetUser),
Ssh2(Ssh2Auth),
}
pub trait HostHandler: Sized {
fn connect(
&mut self,
endpoint: &str,
) -> Result<(), RegentError>;
fn is_connected(&mut self) -> bool;
fn disconnect(&mut self) -> Result<(), RegentError>;
fn is_this_command_available(
&mut self,
command: &str,
privilege: &Privilege,
) -> Result<bool, RegentError>;
fn run_command(
&mut self,
command: &str,
privilege: &Privilege,
) -> Result<CommandResult, RegentError>;
fn run_windows_command(&mut self, command: &str) -> Result<CommandResult, RegentError>;
fn get_file(&mut self, path: PathBuf) -> Result<Vec<u8>, RegentError>;
}
#[derive(Clone, Debug)]
pub enum Handler {
LocalHost(LocalHostHandler),
Ssh2(Ssh2HostHandler),
}
impl Handler {
pub fn localhost(localhost_handler: LocalHostHandler) -> Self {
Handler::LocalHost(localhost_handler)
}
pub fn ss2(ss2_handler: Ssh2HostHandler) -> Self {
Handler::Ssh2(ss2_handler)
}
}
impl HostHandler for Handler {
fn connect(
&mut self,
endpoint: &str,
) -> Result<(), RegentError> {
match self {
Handler::LocalHost(handler) => handler.connect(endpoint),
Handler::Ssh2(handler) => handler.connect(endpoint),
}
}
fn is_connected(&mut self) -> bool {
match self {
Handler::LocalHost(handler) => handler.is_connected(),
Handler::Ssh2(handler) => handler.is_connected(),
}
}
fn disconnect(&mut self) -> Result<(), RegentError> {
match self {
Handler::LocalHost(handler) => handler.disconnect(),
Handler::Ssh2(handler) => handler.disconnect(),
}
}
fn is_this_command_available(
&mut self,
command: &str,
privilege: &Privilege,
) -> Result<bool, RegentError> {
match self {
Handler::LocalHost(handler) => handler.is_this_command_available(command, privilege),
Handler::Ssh2(handler) => handler.is_this_command_available(command, privilege),
}
}
fn run_command(
&mut self,
command: &str,
privilege: &Privilege,
) -> Result<CommandResult, RegentError> {
match self {
Handler::LocalHost(handler) => handler.run_command(command, privilege),
Handler::Ssh2(handler) => handler.run_command(command, privilege),
}
}
fn run_windows_command(&mut self, command: &str) -> Result<CommandResult, RegentError> {
match self {
Handler::LocalHost(handler) => handler.run_windows_command(command),
Handler::Ssh2(handler) => handler.run_windows_command(command),
}
}
fn get_file(&mut self, path: PathBuf) -> Result<Vec<u8>, RegentError> {
match self {
Handler::LocalHost(handler) => handler.get_file(path),
Handler::Ssh2(handler) => handler.get_file(path),
}
}
}
pub fn final_command(cmd: &str, privilege: &Privilege, user: &WhichUser) -> String {
match user {
WhichUser::CurrentUser => match privilege {
Privilege::None => format!("{} 2>&1", cmd),
Privilege::WithSudo => format!("sudo {} 2>&1", cmd),
Privilege::WithSudoRs => format!("sudo-rs {} 2>&1", cmd),
},
WhichUser::UsernamePassword(credentials) => match privilege {
Privilege::None => format!(
"echo {} | su - {} -c \"{}\" 2>&1", credentials.password(),
credentials.username(),
cmd
),
Privilege::WithSudo => format!(
"echo {} | sudo -S -u {} {} 2>&1",
credentials.password(),
credentials.username(),
cmd
),
Privilege::WithSudoRs => format!(
"echo {} | sudo-rs -S -u {} {} 2>&1",
credentials.password(),
credentials.username(),
cmd
),
},
}
}