index : bitwarden-ssh-agent.git

ascending towards madness

use std::env;
use gumdrop::Options;
use anyhow::Result;

mod bwutil;

/// Represents command-line parameters.
#[derive(Debug, Options)]
struct Cli {
	#[options(help = "print help message and exit")]
    help: bool,
	#[options(help = "show debug output")]
	debug: bool,
	#[options(help = "folder name to use to search for SSH keys", default = "ssh-agent")]
	folder: String,
	#[options(help = "custom field name where the private key is stored", meta = "PRIVATE_KEY", default = "private-key")]
	key: String,
	#[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: String,
	#[options(help = "print version and exit")]
    version: bool,
}

/// 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();
	if args.version {
		let name = env!("CARGO_PKG_NAME");
		let version = env!("CARGO_PKG_VERSION");
		println!("{} {}", name, version);
		return Ok(());
	}

	let session_token: String = check_session_token(&args)?;
	if !session_token.is_empty() {
		let folders = bwutil::exec_folder_search(&session_token, &args.folder)?;
		// Retrieve items from each folder since there may be multiple folders with the same name.
		for folder in folders {
			let folder_items = bwutil::exec_list_folder_items(&session_token, &folder.id)?;
			for item in folder_items {
				println!("{:#?}", item);
			}
		}
	}

	Ok(())
}

/// Gets a session token from the active Bitwarden session. The checking process is as follows:
/// 1. Check for `--session` argument and use that.
/// 2. Check for `BW_SESSION` environment variable and use that.
/// 3. Check for login status from `bw`
/// 4. Login or unlock based on login status and use that.
/// An invalid `BW_SESSION` or `--session` will prompt a login.
fn check_session_token(args: &Cli) -> Result<String> {
	println!("Getting Bitwarden session...");
	let mut session_token: String = String::new();
	// Get session flag from the user
	if args.session.trim().is_empty() {
		// No session flag, check for an environment variable
		let env_key = env::var(SESSION_ENV_KEY);
		match env_key {
			Ok(key) => {
				println!("{} is set. Reusing existing session.", SESSION_ENV_KEY);
				// We found it, set our session key
				session_token.push_str(&key);
			},
			Err(_) => {
				// We don't have a token to reuse, so get it from Bitwarden
				println!("{} is not set. Attempting to login.", SESSION_ENV_KEY);
				let token: &String = &bwutil::get_session_token()?;

				if !token.is_empty() {
					println!("Successfully unlocked. To re-use this session, run:");
					println!("export {}=\"{}\"", SESSION_ENV_KEY, token);
				}
				session_token.push_str(token)
			}
		}
	}
	else {
		// Session flag is already defined, pass it on
		session_token.push_str(&args.session);
	}

	Ok(session_token)
}