use anyhow::{anyhow, Context, Result};
use colored::*;
use serde::Deserialize;
use std::process::{Command, Output, Stdio};
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BitwardenFolder {
pub id: String,
pub name: String,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BitwardenItem {
pub id: String,
pub name: String,
pub attachments: Option<Vec<BitwardenAttachment>>,
pub fields: Option<Vec<BitwardenFieldItem>>,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BitwardenFieldItem {
pub name: String,
pub value: Option<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BitwardenAttachment {
pub id: String,
pub file_name: String,
}
pub fn is_logged_in() -> Result<bool> {
let logged_in = Command::new("bw").arg("login").arg("--check").output()?;
Ok(logged_in.stdout.len() > 0)
}
pub fn get_session_token() -> Result<String> {
let mut token = String::new();
let mut operation = String::new();
if is_logged_in()? {
println!("Vault is locked.");
operation.push_str("unlock")
} else {
println!("You are not logged in.");
operation.push_str("login")
}
let success: Output = exec_piped_command("bw", ["--raw", &operation].to_vec());
if success.status.success() {
token.push_str(&String::from_utf8(success.stdout)?);
}
Ok(token)
}
pub fn exec_folder_search(session: &str, folder_name: &str) -> Result<Vec<BitwardenFolder>> {
let folders: Output = exec_piped_command(
"bw",
[
"list",
"folders",
"--search",
&folder_name,
"--session",
&session,
]
.to_vec(),
);
let result: String = String::from_utf8_lossy(&folders.stdout).to_string();
if result.is_empty() {
Err(anyhow!(
"Could not authenticate. This could mean your session has expired.".red()
))
} else {
Ok(serde_json::from_str(&result)
.with_context(|| "Could not deserialize folder search results.")?)
}
}
pub fn exec_list_folder_items(session: &str, folder_id: &str) -> Result<Vec<BitwardenItem>> {
let items = exec_piped_command(
"bw",
[
"list",
"items",
"--folderid",
&folder_id,
"--session",
&session,
]
.to_vec(),
);
let result = String::from_utf8_lossy(&items.stdout).to_string();
if result.is_empty() {
Err(anyhow!(
"Could not authenticate. This could mean your session has expired.".red()
))
} else {
Ok(serde_json::from_str(&result)
.with_context(|| "Could not deserialize item search results.")?)
}
}
fn exec_piped_command(cmd: &str, args: Vec<&str>) -> Output {
let cli_command = match Command::new(cmd).args(&args).stdout(Stdio::piped()).spawn() {
Err(err) => panic!("Error spawning: {}", err),
Ok(process) => process,
};
let output = cli_command.wait_with_output().unwrap();
return output;
}