Files
bitwarden-proton-sync/src/pass.rs
T

189 lines
4.9 KiB
Rust

use crate::sh;
use regex::Regex;
use serde::Deserialize;
use std::io::{Error, ErrorKind};
const EXECUTABLE: &str = "pass-cli";
const EMAIL_REGEX: &str = r"^[\w\-\.]+@([\w-]+\.)+[\w-]{2,}$";
#[derive(Deserialize, Debug)]
pub struct Vaults {
pub vaults: Vec<Vault>,
}
#[derive(Deserialize, Debug)]
pub struct Vault {
pub name: String,
}
#[derive(Deserialize, Debug)]
pub struct Items {
pub items: Vec<Item>,
}
#[derive(Deserialize, Debug)]
pub struct Item {
pub id: String,
pub content: ItemContent,
pub state: String,
}
#[derive(Deserialize, Debug, Clone)]
pub struct ItemContent {
pub title: String,
pub note: String,
pub content: ItemContentContent,
}
#[derive(Deserialize, Debug, Clone)]
pub struct ItemContentContent {
#[serde(rename = "Login")]
pub login: Option<ItemLogin>,
}
// pass-cli provided structure, this is what it's called inside the JSON data
#[derive(Deserialize, Debug, Clone)]
pub struct ItemLogin {
pub email: String,
pub username: String,
pub password: String,
pub urls: Vec<String>,
pub totp_uri: Option<String>,
}
// our own custom structure, a counterpart to rbw::LoginItem
#[derive(Debug, Clone)]
pub struct LoginItem {
pub id: String,
pub title: String,
pub username: String,
pub email: String,
pub password: String,
pub urls: Option<Vec<String>>,
pub notes: String,
pub totp: Option<String>,
}
pub fn check_pass() -> Result<(), Error> {
let which = sh::sh(format!("which {}", EXECUTABLE));
if which.is_empty() {
return Err(Error::new(
ErrorKind::Other,
format!("{} is not installed", EXECUTABLE),
));
}
let test = sh::sh(format!("{} test", EXECUTABLE));
if test != "Connection successful\n" {
return Err(Error::new(
ErrorKind::Other,
format!("{} test failed", EXECUTABLE),
));
}
Ok(())
}
pub fn get_vaults() -> Result<Vaults, Error> {
let vaults_raw = sh::sh(format!("{} vault list --output json", EXECUTABLE));
match serde_json::from_str(vaults_raw.as_str()) {
Ok(val) => Ok(val),
Err(e) => Err(Error::new(
ErrorKind::Other,
format!("Couldn't get vaults: {e}"),
)),
}
}
pub fn get_items(vault: &String) -> Result<Items, Error> {
let items_raw = sh::sh(format!(
"{} item list '{}' --output json --show-secrets",
EXECUTABLE, vault
));
match serde_json::from_str(items_raw.as_str()) {
Ok(val) => Ok(val),
Err(e) => Err(Error::new(
ErrorKind::Other,
format!("Couldn't get items: {e}"),
)),
}
}
pub fn get_logins(items: Items) -> Vec<LoginItem> {
items
.items
.iter()
.filter(|item| {
if item.state.to_lowercase() != "active" {
false
} else if let None = item.content.content.login {
false
} else {
true
}
})
.map(|item| {
let login = item.content.content.login.clone().unwrap();
LoginItem {
id: item.id.clone(),
title: item.content.title.clone(),
username: login.username,
email: login.email,
password: login.password,
notes: item.content.note.clone(),
urls: Some(login.urls),
totp: login.totp_uri,
}
})
.collect()
}
pub fn update(vault: &String, item: LoginItem, user_is_actually_email: bool) {
let user_field_update = if user_is_actually_email {
format!("email={}", item.email)
} else {
format!("usename={}", item.username)
};
let maybe_urls = if let Some(urls) = item.urls {
let mut it = "".to_string();
for url in urls {
it = format!("{} --field 'urls={}'", it, url);
}
it
} else {
"".to_string()
};
let cmd = format!(
"{} item update --vault-name '{}' --item-id '{}' --field 'password={}' --field '{}' --field 'note={}' {}",
EXECUTABLE, vault, item.id, item.password, user_field_update, item.notes, maybe_urls
);
let output = sh::sh(cmd.clone());
println!("> {}", output);
}
pub fn create(vault: &String, title: String, user: String, password: String) {
let re = Regex::new(EMAIL_REGEX).expect("Couldn't parse regex");
let is_email = re.is_match(&user);
let user_arg = if is_email {
format!("--email '{}'", user)
} else {
format!("--username '{}'", user)
};
let output = sh::sh(format!(
"{} item create login --vault-name '{}' --title '{}' {} --password '{}'",
EXECUTABLE, vault, title, user_arg, password
));
println!("> {}", output);
}
pub fn trash(vault: &String, id: String) {
let output = sh::sh(format!(
"{} item trash --vault-name '{}' --item-id '{}'",
EXECUTABLE, vault, id
));
println!("> {}", output);
}