use std::path::Path; use async_ssh2_tokio::Config; use reqwest::Client; use reqwest::header::{HeaderName, HeaderValue}; use async_trait::async_trait; use async_ssh2_tokio::client::{self, AuthMethod, Client as SSHClient, ServerCheckMethod}; use rocket::futures::TryFutureExt; use rocket::tokio::fs; use serde::Deserialize; use enum_dispatch::enum_dispatch; use crate::app_config::CONFIG; use crate::errors::AutoDecryptError; #[derive(Debug, Deserialize)] pub(crate) enum ConsentMethode { None, Boolean, PassheyHash(String), } #[derive(Debug, Deserialize)] #[enum_dispatch(ProviderAction)] pub(crate) enum ActionType { HTTPRequest(HTTPAction), SSHRequest(SSHAction), EncryptedRequest(EncryptedAction) } #[derive(Debug, Deserialize)] pub struct Providers { pub(crate) access_key_hash: String, pub(crate) consent_methode: ConsentMethode, pub(crate) execution_action: ActionType, // if action in seperate file and constent methode is passkey, we try // to decrypt the action file with the passkey } #[derive(Debug, Deserialize)] struct HTTPAction { pub(crate) method: String, pub(crate) uri: String, pub(crate) headers: Option>, pub(crate) body: Option, } #[async_trait] impl ProviderAction for HTTPAction { async fn execute(&self) -> Result { let client = Client::new(); let mut req_builder = client.request( reqwest::Method::from_bytes(self.method.as_bytes()) .map_err(|e| e.to_string())?, &self.uri, ); if let Some(headers) = &self.headers { for (key, value) in headers { req_builder = req_builder.header( HeaderName::from_bytes(key.as_bytes()).map_err(|e| e.to_string())?, HeaderValue::from_str(value).map_err(|e| e.to_string())?, ); } } let response = req_builder.send().await .map_err(|e| e.to_string())?; Ok(format!("Success! Server replied with: {}", response.text().await.map_err(|e| e.to_string())?)) } } #[derive(Debug, Deserialize)] struct SSHAction { pub(crate) uri: String, pub(crate) port: u16, pub(crate) username: String, pub(crate) private_key: String, pub(crate) cmd: String, } #[async_trait] impl ProviderAction for SSHAction { async fn execute(&self) -> Result { let auth_method = AuthMethod::PrivateKey { key_data: self.private_key.clone(), key_pass: None }; let client = SSHClient::connect( (&*self.uri, self.port), &self.username, auth_method, ServerCheckMethod::KnownHostsFile(CONFIG.wait().ssh_known_host_file.clone()), ).await.map_err(|e| e.to_string())?; let result = client.execute(&self.cmd).await.map_err(|e| e.to_string())?; if result.exit_status == 0 { Ok(result.stdout) } else { Err(result.stderr) } } } #[derive(Debug, Deserialize)] pub struct EncryptedAction{ pub(crate) filename: String, pub(crate) content: Option } impl ProviderAction for EncryptedAction {} impl EncryptedAction { pub(crate) fn load_from_file(&mut self, find_in_dir: &Path) -> Result<&mut Self, AutoDecryptError> { let content = std::fs::read_to_string(find_in_dir.join(&self.filename)) .map_err(|err| AutoDecryptError::ConfigurationError { comment: err.to_string() })?; self.content = Some(content); Ok(self) } async fn to_executable_action(&self, key: &str) -> Result { todo!() //let cleartext_toml = //1. get Cleartext toml 2. load tomel as any other 3. return object } } #[async_trait] #[enum_dispatch] pub (crate) trait ProviderAction: std::fmt::Debug + Send + Sync { async fn execute(&self) -> Result { return Err("We are not directly executable!".to_string()) } }