From 0311ba3ced771a76fa8bd7614dd98271e518042a Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 10 Jan 2020 19:16:02 +0530 Subject: [PATCH 1/3] Add ability to read pass from env variables --- tiny/Cargo.toml | 1 + tiny/src/config.rs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tiny/Cargo.toml b/tiny/Cargo.toml index 6dd40bee..168e876d 100644 --- a/tiny/Cargo.toml +++ b/tiny/Cargo.toml @@ -27,6 +27,7 @@ libtiny_wire = { path = "../libtiny_wire" } log = "0.4" serde = { version = "1.0.8", features = ["derive"] } serde_yaml = "0.7.1" +shellexpand = "1.1.0" time = "0.1" tokio = { version = "0.2.11", features = ["full"] } diff --git a/tiny/src/config.rs b/tiny/src/config.rs index ac64d1b9..1c1259fe 100644 --- a/tiny/src/config.rs +++ b/tiny/src/config.rs @@ -1,4 +1,5 @@ -use serde::Deserialize; +use serde::{de::Error, Deserialize, Deserializer}; +use serde_yaml; use std::fs; use std::fs::File; use std::io::{Read, Write}; @@ -27,7 +28,7 @@ pub(crate) struct Server { pub(crate) tls: bool, /// Server password (optional) - #[serde(default)] + #[serde(default, deserialize_with = "with_expand_envs")] pub(crate) pass: Option, /// Real name to be used in connection registration @@ -90,6 +91,18 @@ pub(crate) fn validate_config(config: &Config) -> Vec { errors } +/// Expand env variables in the config (used primarily for pass) +fn with_expand_envs<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + match shellexpand::env(&s) { + Ok(value) => Ok(Some(value.to_string())), + Err(err) => Err(Error::custom(err)), + } +} + /// Returns tiny config file path. File may or may not exist. /// /// Places to look: (in priority order) From 8bc9caf42113fafad4ab4f56bb0aaf1dba05ecdf Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Tue, 21 Jan 2020 16:07:05 +0530 Subject: [PATCH 2/3] switch from shellexapnd to custom env var expantion --- Cargo.lock | 1 + tiny/Cargo.toml | 2 +- tiny/src/config.rs | 31 ++++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5af81199..f71de515 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,6 +1291,7 @@ dependencies = [ "libtiny_ui", "libtiny_wire", "log", + "regex", "rustc_tools_util", "serde", "serde_yaml", diff --git a/tiny/Cargo.toml b/tiny/Cargo.toml index 168e876d..d577613c 100644 --- a/tiny/Cargo.toml +++ b/tiny/Cargo.toml @@ -27,7 +27,7 @@ libtiny_wire = { path = "../libtiny_wire" } log = "0.4" serde = { version = "1.0.8", features = ["derive"] } serde_yaml = "0.7.1" -shellexpand = "1.1.0" +regex = "1" time = "0.1" tokio = { version = "0.2.11", features = ["full"] } diff --git a/tiny/src/config.rs b/tiny/src/config.rs index 1c1259fe..c2f1b899 100644 --- a/tiny/src/config.rs +++ b/tiny/src/config.rs @@ -1,5 +1,7 @@ -use serde::{de::Error, Deserialize, Deserializer}; +use regex::Regex; +use serde::{Deserialize, Deserializer}; use serde_yaml; +use std::env; use std::fs; use std::fs::File; use std::io::{Read, Write}; @@ -11,7 +13,7 @@ pub(crate) struct SASLAuth { pub(crate) password: String, } -#[derive(Clone, Deserialize)] +#[derive(Clone, Deserialize, Debug)] pub(crate) struct Server { /// Address of the server pub(crate) addr: String, @@ -51,7 +53,7 @@ pub(crate) struct Server { } /// Similar to `Server`, but used when connecting via the `/connect` command. -#[derive(Clone, Deserialize)] +#[derive(Clone, Deserialize, Debug)] pub(crate) struct Defaults { pub(crate) nicks: Vec, pub(crate) realname: String, @@ -61,7 +63,7 @@ pub(crate) struct Defaults { pub(crate) tls: bool, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub(crate) struct Config { pub(crate) servers: Vec, pub(crate) defaults: Defaults, @@ -91,15 +93,30 @@ pub(crate) fn validate_config(config: &Config) -> Vec { errors } +fn is_shell_var(val: &str) -> bool { + let re = Regex::new(r"^\$[A-z][A-z0-9_]+$").unwrap(); + return re.is_match(val); +} + /// Expand env variables in the config (used primarily for pass) fn with_expand_envs<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - match shellexpand::env(&s) { - Ok(value) => Ok(Some(value.to_string())), - Err(err) => Err(Error::custom(err)), + if is_shell_var(&s) { + return match env::var(&s[1..]) { + Ok(value) => Ok(Some(value.to_string())), + Err(_) => { + eprintln!("Tried expanding env variable {} but unable to find it. If this is your password and not and env variable, add the password as ${}", &s, &s); + std::process::exit(1); + } + }; + } else { + if &s[0..2] == "$$" { + return Ok(Some(s[1..].to_string())); + } + Ok(Some(s)) } } From f31f185503e47c3a46a3071cb5f1a0129417ed06 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Sat, 22 Aug 2020 15:08:43 +0530 Subject: [PATCH 3/3] use ! for cmd in pass --- tiny/src/config.rs | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tiny/src/config.rs b/tiny/src/config.rs index c2f1b899..1c97b748 100644 --- a/tiny/src/config.rs +++ b/tiny/src/config.rs @@ -1,11 +1,11 @@ use regex::Regex; use serde::{Deserialize, Deserializer}; use serde_yaml; -use std::env; use std::fs; use std::fs::File; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; +use std::process::{exit, Command}; #[derive(Clone, Deserialize, Debug, PartialEq, Eq)] pub(crate) struct SASLAuth { @@ -93,8 +93,8 @@ pub(crate) fn validate_config(config: &Config) -> Vec { errors } -fn is_shell_var(val: &str) -> bool { - let re = Regex::new(r"^\$[A-z][A-z0-9_]+$").unwrap(); +fn is_command(val: &str) -> bool { + let re = Regex::new(r"^![^!].*$").unwrap(); return re.is_match(val); } @@ -104,16 +104,36 @@ where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; - if is_shell_var(&s) { - return match env::var(&s[1..]) { - Ok(value) => Ok(Some(value.to_string())), + if is_command(&s) { + let output = Command::new("sh").arg("-c").arg(&s[1..]).output(); + + match output { + Ok(value) => { + if value.status.success() { + println!("{}", String::from_utf8_lossy(&value.stderr).to_string()); + let mut value_str = String::from_utf8_lossy(&value.stdout).to_string(); + if value_str.ends_with('\n') { + // Remove extra newline from cli output + value_str.pop(); + } + Ok(Some(value_str)) + } else { + println!( + "Failed to get password from command `{}`: \n{}", + &s[1..], + String::from_utf8_lossy(&value.stderr).to_string() + ); + println!("NOTE: You can use '!!' to escape if your password starts with '!'"); + exit(1); + } + } Err(_) => { - eprintln!("Tried expanding env variable {} but unable to find it. If this is your password and not and env variable, add the password as ${}", &s, &s); - std::process::exit(1); + println!("Failed to get password from command `{}`", &s[1..]); + exit(1); } - }; + } } else { - if &s[0..2] == "$$" { + if &s[0..2] == "!!" { return Ok(Some(s[1..].to_string())); } Ok(Some(s))