🔨 Refactors sync to use bw instead of rbw
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
use crate::sh;
|
||||
use serde::Deserialize;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
const EXECUTABLE: &str = "bw";
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Item {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[serde(rename = "passwordHistory")]
|
||||
pub password_history: Vec<PasswordHistoryItem>,
|
||||
#[serde(rename = "revisionDate")]
|
||||
pub revision_date: String,
|
||||
#[serde(rename = "creationDate")]
|
||||
pub creation_date: String,
|
||||
pub object: String,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: u16,
|
||||
pub notes: Option<String>,
|
||||
pub favorite: bool,
|
||||
pub fields: Vec<ItemField>,
|
||||
pub login: Login,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct PasswordHistoryItem {
|
||||
#[serde(rename = "lastUsedDate")]
|
||||
pub last_used_date: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct ItemField {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: u16,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Login {
|
||||
pub uris: Vec<LoginUri>,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub totp: Option<String>,
|
||||
#[serde(rename = "passwordRevisionDate")]
|
||||
pub password_revision_date: Option<String>,
|
||||
#[serde(rename = "fido2Credentials")]
|
||||
pub fido2_credentials: Vec<Fido2Credential>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct LoginUri {
|
||||
pub uri: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Fido2Credential {
|
||||
#[serde(rename = "credentialId")]
|
||||
pub credential_id: String,
|
||||
#[serde(rename = "keyType")]
|
||||
pub key_type: String,
|
||||
#[serde(rename = "keyAlgorithm")]
|
||||
pub key_algorithm: String,
|
||||
#[serde(rename = "keyCurve")]
|
||||
pub key_curve: String,
|
||||
#[serde(rename = "keyValue")]
|
||||
pub key_value: String,
|
||||
#[serde(rename = "rpId")]
|
||||
pub rp_id: String,
|
||||
#[serde(rename = "userHandle")]
|
||||
pub user_handle: String,
|
||||
#[serde(rename = "userName")]
|
||||
pub user_name: String,
|
||||
pub counter: String,
|
||||
#[serde(rename = "rpName")]
|
||||
pub rp_name: String,
|
||||
#[serde(rename = "userDisplayName")]
|
||||
pub user_display_name: String,
|
||||
pub discoverable: String,
|
||||
#[serde(rename = "creationDate")]
|
||||
pub creation_date: String,
|
||||
}
|
||||
|
||||
pub fn check_bw() -> Result<String, Error> {
|
||||
let which = sh::sh(format!("which {}", EXECUTABLE));
|
||||
if which.is_empty() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("{} is not installed", EXECUTABLE),
|
||||
));
|
||||
}
|
||||
|
||||
let session = sh::sh(format!("{} unlock", EXECUTABLE));
|
||||
if session.is_empty() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("{} couldn't unlock", EXECUTABLE),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(session)
|
||||
}
|
||||
|
||||
pub fn get_items(session: String) -> Result<Vec<Item>, Error> {
|
||||
let items_raw = sh::sh(format!("BW_SESSION={} {} list --raw", session, EXECUTABLE));
|
||||
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}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
+30
-32
@@ -1,11 +1,11 @@
|
||||
mod bw;
|
||||
mod pass;
|
||||
mod rbw;
|
||||
mod sh;
|
||||
|
||||
use clap::Parser;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::io::{Error, ErrorKind, Write};
|
||||
use clap::Parser;
|
||||
|
||||
const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
||||
|
||||
@@ -22,12 +22,12 @@ fn ask_consent(question: String, line1: String, line2: String) -> Result<bool, E
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input)?;
|
||||
|
||||
return Ok(input.to_lowercase() == "y\n");
|
||||
Ok(input.to_lowercase() == "y\n")
|
||||
}
|
||||
|
||||
fn sync() -> Result<(), Error> {
|
||||
pass::check_pass()?;
|
||||
rbw::check_rbw()?;
|
||||
let bw_session = bw::check_bw()?;
|
||||
|
||||
let vaults = pass::get_vaults()?.vaults;
|
||||
if vaults.len() <= 0 {
|
||||
@@ -62,21 +62,20 @@ fn sync() -> Result<(), Error> {
|
||||
let pass_items = pass::get_items(&vault)?;
|
||||
let mut pass_logins = pass::get_logins(pass_items);
|
||||
|
||||
let rbw_items = rbw::get_items()?;
|
||||
let mut rbw_logins = rbw::get_logins(rbw_items)?;
|
||||
let mut bw_items = bw::get_items(bw_session)?;
|
||||
|
||||
println!();
|
||||
println!(
|
||||
"Got {} rbw logins and {} pass logins...",
|
||||
rbw_logins.len(),
|
||||
"Got {} bw logins and {} pass logins...",
|
||||
bw_items.len(),
|
||||
pass_logins.len()
|
||||
);
|
||||
|
||||
for rbw_login in rbw_logins.clone().iter() {
|
||||
for bw_item in bw_items.clone().iter() {
|
||||
let mut pass_login: Option<&pass::LoginItem> = None;
|
||||
|
||||
'inner: for pass_login_iter in pass_logins.iter() {
|
||||
if rbw_login.name == pass_login_iter.title {
|
||||
if bw_item.name == pass_login_iter.title {
|
||||
pass_login = Some(pass_login_iter);
|
||||
break 'inner;
|
||||
}
|
||||
@@ -87,39 +86,39 @@ fn sync() -> Result<(), Error> {
|
||||
}
|
||||
let pass_login = pass_login.unwrap();
|
||||
|
||||
let rbw_password = rbw_login.password.clone();
|
||||
let bw_password = bw_item.login.password.clone();
|
||||
let pass_password = pass_login.password.clone();
|
||||
|
||||
let rbw_user = rbw_login.user.clone();
|
||||
let bw_user = bw_item.login.username.clone();
|
||||
let pass_user = match pass_login.username.is_empty() {
|
||||
true => pass_login.email.clone(),
|
||||
false => pass_login.username.clone(),
|
||||
};
|
||||
let pass_user_is_actually_email = pass_user == pass_login.email;
|
||||
|
||||
if rbw_user != pass_user || rbw_password != pass_password {
|
||||
if bw_user != pass_user || bw_password != pass_password {
|
||||
let consent = ask_consent(
|
||||
format!("Attempting to update {}:", rbw_login.name),
|
||||
format!("{} -> {}", pass_user, rbw_user.clone()),
|
||||
format!("{} -> {}", pass_password, rbw_password.clone()),
|
||||
format!("Attempting to update {}:", bw_item.name),
|
||||
format!("{} -> {}", pass_user, bw_user.clone()),
|
||||
format!("{} -> {}", pass_password, bw_password.clone()),
|
||||
)?;
|
||||
if consent {
|
||||
let updated_pass_login = pass::LoginItem {
|
||||
id: pass_login.id.clone(),
|
||||
title: pass_login.title.clone(),
|
||||
username: rbw_user.clone(),
|
||||
email: rbw_user.clone(),
|
||||
password: rbw_password,
|
||||
username: bw_user.clone(),
|
||||
email: bw_user.clone(),
|
||||
password: bw_password,
|
||||
};
|
||||
pass::update(&vault, updated_pass_login, pass_user_is_actually_email);
|
||||
}
|
||||
}
|
||||
|
||||
let rbw_index = rbw_logins
|
||||
let bw_index = bw_items
|
||||
.iter()
|
||||
.position(|x| x.id == rbw_login.id)
|
||||
.position(|x| x.id == bw_item.id)
|
||||
.unwrap();
|
||||
rbw_logins.remove(rbw_index);
|
||||
bw_items.remove(bw_index);
|
||||
|
||||
let pass_index = pass_logins
|
||||
.iter()
|
||||
@@ -128,22 +127,21 @@ fn sync() -> Result<(), Error> {
|
||||
pass_logins.remove(pass_index);
|
||||
}
|
||||
|
||||
// TODO: need to delete these
|
||||
println!();
|
||||
println!(
|
||||
"Remaining {} rbw logins and {} pass logins...",
|
||||
rbw_logins.len(),
|
||||
"Remaining {} bw logins and {} pass logins...",
|
||||
bw_items.len(),
|
||||
pass_logins.len()
|
||||
);
|
||||
|
||||
for rbw_login in rbw_logins {
|
||||
for bw_item in bw_items {
|
||||
let consent = ask_consent(
|
||||
format!("Attempting to create {}:", rbw_login.name),
|
||||
format!("{}", rbw_login.user),
|
||||
format!("{}", rbw_login.password),
|
||||
format!("Attempting to create {}:", bw_item.name),
|
||||
format!("{}", bw_item.login.username),
|
||||
format!("{}", bw_item.login.password),
|
||||
)?;
|
||||
if consent {
|
||||
pass::create(&vault, rbw_login.name, rbw_login.user, rbw_login.password);
|
||||
pass::create(&vault, bw_item.name, bw_item.login.username, bw_item.login.password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,11 +163,11 @@ fn sync() -> Result<(), Error> {
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
Cli::parse();
|
||||
sync()?;
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+5
-5
@@ -75,18 +75,18 @@ pub fn check_pass() -> Result<(), Error> {
|
||||
));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_vaults() -> Result<Vaults, Error> {
|
||||
let vaults_raw = sh::sh(format!("{} vault list --output json", EXECUTABLE));
|
||||
return match serde_json::from_str(vaults_raw.as_str()) {
|
||||
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> {
|
||||
@@ -94,13 +94,13 @@ pub fn get_items(vault: &String) -> Result<Items, Error> {
|
||||
"{} item list '{}' --output json --show-secrets",
|
||||
EXECUTABLE, vault
|
||||
));
|
||||
return match serde_json::from_str(items_raw.as_str()) {
|
||||
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> {
|
||||
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
use crate::sh;
|
||||
use serde::Deserialize;
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
const EXECUTABLE: &str = "rbw";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Item {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub user: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct LoginItem {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
pub fn check_rbw() -> 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 unlocked = sh::sh(format!("{} unlocked", EXECUTABLE));
|
||||
if !unlocked.is_empty() {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("{} database not unlocked", EXECUTABLE),
|
||||
));
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn get_items() -> Result<Vec<Item>, Error> {
|
||||
let items_raw = sh::sh(format!("{} list --raw", EXECUTABLE));
|
||||
return 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: Vec<Item>) -> Result<Vec<LoginItem>, Error> {
|
||||
let items = items
|
||||
.iter()
|
||||
.filter(|item| item.type_.to_lowercase() == "login")
|
||||
.collect::<Vec<&Item>>();
|
||||
let mut login_items = Vec::<LoginItem>::new();
|
||||
|
||||
for item in items {
|
||||
let password = sh::sh(format!("{} get '{}'", EXECUTABLE, item.id));
|
||||
if let None = item.user {
|
||||
println!("WARNING: user not found for {}", item.name);
|
||||
}
|
||||
|
||||
login_items.push(LoginItem {
|
||||
id: item.id.clone(),
|
||||
name: item.name.clone(),
|
||||
user: match item.user.clone() {
|
||||
Some(val) => val,
|
||||
None => "".to_string(),
|
||||
},
|
||||
password: match password.strip_suffix("\n") {
|
||||
Some(s) => s.to_string(),
|
||||
None => password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(login_items);
|
||||
}
|
||||
Reference in New Issue
Block a user