index : bitwarden-ssh-agent.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2023-07-28 20:47:04.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2023-07-28 20:47:04.0 +00:00:00
commit
d82c208953f9ec7e04bba01ecb587584169d18b5 [patch]
tree
1a987201890eb1c12c1745a822a83ea942123f7c
parent
461918e09cc9f152d842e1a751e6ad3c01ee6b54
download
d82c208953f9ec7e04bba01ecb587584169d18b5.tar.gz

feat: add retrieving and creating bitwarden sessions



Diff

 src/main.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 97 insertions(+), 3 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 92be148..31f9632 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,8 @@
use std::{env, process::{Command, Stdio, Output}};
use gumdrop::Options;
use anyhow::Result;

/// Represents command-line parameters.
#[derive(Debug, Options)]
struct Cli {
	#[options(help = "print help message and exit")]
@@ -13,12 +16,103 @@ struct Cli {
	#[options(help = "custom field name where the key passphrase is stored", meta = "PASS", default = "passphrase")]
	passphrase: String,
	#[options(help = "session to use to log in to bitwarden-cli")]
	session: Option<String>,
	session: String,
	#[options(help = "print version and exit")]
    version: bool,
}

fn main() {
/// Environment variable housing an existing Bitwarden session.
const SESSION_ENV_KEY: &str = "BW_SESSION";

fn main() -> Result<()> {
    let args: Cli = Cli::parse_args_default_or_exit();
	println!("{:#?}", args);
	if args.version {
		let name = env!("CARGO_PKG_NAME");
		let version = env!("CARGO_PKG_VERSION");
		println!("{} {}", name, version);
		return Ok(());
	}

	let session: String = get_session(&args)?;
	if !session.is_empty() {
		println!("Successfully unlocked. To re-use this session, run:");
		println!("export {}=\"{}\"", SESSION_ENV_KEY, session);
	}

	Ok(())
}

/// Gets the active Bitwarden session. The checking process is as follows:
/// 1. Check for `--session` argument
/// 2. Check for `BW_SESSION` environment variable
/// 3. Check for login status from `bw`
/// 4. Login or unlock based on login status
fn get_session(args: &Cli) -> Result<String> {
	println!("Getting Bitwarden session...");
	let mut session_key: String = String::new();
	// Get session flag from the user
	if args.session.trim().is_empty() {
		// No session flag, check for an environment variable
		let key = env::var(SESSION_ENV_KEY);
		match key {
			Ok(key) => {
				println!("Existing Bitwarden session found.");
				// We found it, set our session key
				session_key.push_str(&key);
			},
			Err(_) => {
				// No session key found. check to see if Bitwarden is unlocked
				let logged_in = Command::new("bw")
				.arg("login")
				.arg("--check")
				.arg("--quiet")
				.output()?;

				let mut operation = String::new();
				// Check for a status code
				if !logged_in.status.success() {
					// No success, use login
					println!("Not logged in.");
					operation.push_str("login");				
				}
				else {
					// Success, use unlock
					println!("Vault is locked.");
					operation.push_str("unlock")
				}

				let success: Output = exec_interactive_command("bw", ["--raw", &operation].to_vec());
				if success.status.success() {
					session_key.push_str(&String::from_utf8(success.stdout)?);
				}
			}
		}
	}
	else {
		// Session flag is defined, pass it on
		session_key.push_str(&args.session);
	}

	Ok(session_key)
}

/// Execute an interactive command.
/// 
/// The resulting output will be returned as `Output`. This is a modified version of:
/// 
/// https://users.rust-lang.org/t/command-if-a-child-process-is-asking-for-input-how-to-forward-the-question-to-the-user/37490/3
pub fn exec_interactive_command(cmd: &str, args: Vec<&str>) -> Output {
    let cli_command = match Command::new(cmd)
        .args(&args)
        .stdin(Stdio::inherit())
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
    {
        Err(err) => panic!("Error spawning: {}", err),
        Ok(process) => process,
    };

	let output = cli_command.wait_with_output().unwrap();
    return output;
}