Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd0d572d3f
|
||
|
|
d3e971fecd
|
||
|
|
22568554af
|
||
|
|
c7aacb1df6
|
||
|
|
bfc24d312d
|
||
|
|
8d4b007c51
|
||
|
|
acfa10b104
|
@@ -0,0 +1,111 @@
|
|||||||
|
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: Option<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: Option<String>,
|
||||||
|
pub password: Option<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: Option<String>,
|
||||||
|
pub discoverable: String,
|
||||||
|
#[serde(rename = "creationDate")]
|
||||||
|
pub creation_date: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_bw() -> Result<(), Error> {
|
||||||
|
let which = sh::sh(format!("which {}", EXECUTABLE));
|
||||||
|
if which.is_empty() {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
format!("{} is not installed", EXECUTABLE),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_items() -> Result<Vec<Item>, Error> {
|
||||||
|
let items_raw = sh::sh(format!("{} list items --raw", EXECUTABLE));
|
||||||
|
match serde_json::from_str::<Vec<Item>>(items_raw.as_str()) {
|
||||||
|
Ok(val) => Ok(val
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.type_ == 1)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<Item>>()),
|
||||||
|
Err(e) => Err(Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
format!("Couldn't get items: {e}"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
-35
@@ -1,11 +1,11 @@
|
|||||||
|
mod bw;
|
||||||
mod pass;
|
mod pass;
|
||||||
mod rbw;
|
|
||||||
mod sh;
|
mod sh;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::{Error, ErrorKind, Write};
|
use std::io::{Error, ErrorKind, Write};
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
||||||
|
|
||||||
@@ -13,21 +13,27 @@ const ENV_VAR_DEFAULT_VAULT: &str = "PASS_VAULT";
|
|||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
struct Cli {}
|
struct Cli {}
|
||||||
|
|
||||||
fn ask_consent(question: String, line1: String, line2: String) -> Result<bool, Error> {
|
fn ask_consent(
|
||||||
|
question: String,
|
||||||
|
line1: String,
|
||||||
|
line2: String,
|
||||||
|
line3: String,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
println!("{}", question);
|
println!("{}", question);
|
||||||
println!("\t{}", line1);
|
println!("\t{}", line1);
|
||||||
println!("\t{}", line2);
|
println!("\t{}", line2);
|
||||||
|
println!("\t{}", line3);
|
||||||
print!("Proceed? [y/N] ");
|
print!("Proceed? [y/N] ");
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
|
|
||||||
return Ok(input.to_lowercase() == "y\n");
|
Ok(input.to_lowercase() == "y\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync() -> Result<(), Error> {
|
fn sync() -> Result<(), Error> {
|
||||||
pass::check_pass()?;
|
pass::check_pass()?;
|
||||||
rbw::check_rbw()?;
|
bw::check_bw()?;
|
||||||
|
|
||||||
let vaults = pass::get_vaults()?.vaults;
|
let vaults = pass::get_vaults()?.vaults;
|
||||||
if vaults.len() <= 0 {
|
if vaults.len() <= 0 {
|
||||||
@@ -62,21 +68,25 @@ fn sync() -> Result<(), Error> {
|
|||||||
let pass_items = pass::get_items(&vault)?;
|
let pass_items = pass::get_items(&vault)?;
|
||||||
let mut pass_logins = pass::get_logins(pass_items);
|
let mut pass_logins = pass::get_logins(pass_items);
|
||||||
|
|
||||||
let rbw_items = rbw::get_items()?;
|
let mut bw_items = bw::get_items()?;
|
||||||
let mut rbw_logins = rbw::get_logins(rbw_items)?;
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"Got {} rbw logins and {} pass logins...",
|
"Got {} bw logins and {} pass logins...",
|
||||||
rbw_logins.len(),
|
bw_items.len(),
|
||||||
pass_logins.len()
|
pass_logins.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
for rbw_login in rbw_logins.clone().iter() {
|
for bw_item in bw_items.clone().iter() {
|
||||||
|
if let None = bw_item.login {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let bw_login = bw_item.login.clone().unwrap();
|
||||||
|
|
||||||
let mut pass_login: Option<&pass::LoginItem> = None;
|
let mut pass_login: Option<&pass::LoginItem> = None;
|
||||||
|
|
||||||
'inner: for pass_login_iter in pass_logins.iter() {
|
'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);
|
pass_login = Some(pass_login_iter);
|
||||||
break 'inner;
|
break 'inner;
|
||||||
}
|
}
|
||||||
@@ -87,39 +97,55 @@ fn sync() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
let pass_login = pass_login.unwrap();
|
let pass_login = pass_login.unwrap();
|
||||||
|
|
||||||
let rbw_password = rbw_login.password.clone();
|
let bw_password = bw_login.password.clone().unwrap_or("".to_string());
|
||||||
let pass_password = pass_login.password.clone();
|
let pass_password = pass_login.password.clone();
|
||||||
|
|
||||||
let rbw_user = rbw_login.user.clone();
|
let bw_user = bw_login.username.clone().unwrap_or("".to_string());
|
||||||
let pass_user = match pass_login.username.is_empty() {
|
let pass_user = match pass_login.username.is_empty() {
|
||||||
true => pass_login.email.clone(),
|
true => pass_login.email.clone(),
|
||||||
false => pass_login.username.clone(),
|
false => pass_login.username.clone(),
|
||||||
};
|
};
|
||||||
let pass_user_is_actually_email = pass_user == pass_login.email;
|
let pass_user_is_actually_email = pass_user == pass_login.email;
|
||||||
|
|
||||||
if rbw_user != pass_user || rbw_password != pass_password {
|
let bw_notes = bw_item.notes.clone().unwrap_or("".to_string());
|
||||||
|
let pass_notes = pass_login.notes.clone();
|
||||||
|
|
||||||
|
if bw_user != pass_user
|
||||||
|
|| (bw_password != pass_password && bw_password != "".to_string())
|
||||||
|
|| bw_notes != pass_notes
|
||||||
|
{
|
||||||
let consent = ask_consent(
|
let consent = ask_consent(
|
||||||
format!("Attempting to update {}:", rbw_login.name),
|
format!("Attempting to update {}:", bw_item.name),
|
||||||
format!("{} -> {}", pass_user, rbw_user.clone()),
|
format!("{} -> {}", pass_user, bw_user.clone()),
|
||||||
format!("{} -> {}", pass_password, rbw_password.clone()),
|
format!("{} -> {}", pass_password, bw_password.clone()),
|
||||||
|
format!("{} -> {}", bw_notes, pass_notes),
|
||||||
)?;
|
)?;
|
||||||
if consent {
|
if consent {
|
||||||
let updated_pass_login = pass::LoginItem {
|
let updated_pass_login = pass::LoginItem {
|
||||||
id: pass_login.id.clone(),
|
id: pass_login.id.clone(),
|
||||||
title: pass_login.title.clone(),
|
title: pass_login.title.clone(),
|
||||||
username: rbw_user.clone(),
|
username: bw_user.clone(),
|
||||||
email: rbw_user.clone(),
|
email: bw_user.clone(),
|
||||||
password: rbw_password,
|
password: bw_password,
|
||||||
|
notes: bw_notes,
|
||||||
|
urls: Some(bw_login.uris.iter().map(|x| x.uri.clone()).collect()),
|
||||||
|
totp: pass_login.totp.clone(),
|
||||||
};
|
};
|
||||||
pass::update(&vault, updated_pass_login, pass_user_is_actually_email);
|
pass::update(&vault, updated_pass_login, pass_user_is_actually_email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let rbw_index = rbw_logins
|
let bw_totp = bw_login.totp.clone().unwrap_or("".to_string());
|
||||||
.iter()
|
let pass_totp = pass_login.totp.clone().unwrap_or("".to_string());
|
||||||
.position(|x| x.id == rbw_login.id)
|
if bw_totp != "".to_string() && pass_totp == "".to_string() {
|
||||||
.unwrap();
|
println!(
|
||||||
rbw_logins.remove(rbw_index);
|
"TOTP found for {} but not in pass, you need to manually change it to {}",
|
||||||
|
bw_item.name, bw_totp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bw_index = bw_items.iter().position(|x| x.id == bw_item.id).unwrap();
|
||||||
|
bw_items.remove(bw_index);
|
||||||
|
|
||||||
let pass_index = pass_logins
|
let pass_index = pass_logins
|
||||||
.iter()
|
.iter()
|
||||||
@@ -128,22 +154,29 @@ fn sync() -> Result<(), Error> {
|
|||||||
pass_logins.remove(pass_index);
|
pass_logins.remove(pass_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: need to delete these
|
|
||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"Remaining {} rbw logins and {} pass logins...",
|
"Remaining {} bw logins and {} pass logins...",
|
||||||
rbw_logins.len(),
|
bw_items.len(),
|
||||||
pass_logins.len()
|
pass_logins.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
for rbw_login in rbw_logins {
|
for bw_item in bw_items {
|
||||||
|
let login = bw_item.login.unwrap();
|
||||||
|
|
||||||
let consent = ask_consent(
|
let consent = ask_consent(
|
||||||
format!("Attempting to create {}:", rbw_login.name),
|
format!("Attempting to create {}:", bw_item.name),
|
||||||
format!("{}", rbw_login.user),
|
format!("{}", login.username.clone().unwrap_or("".to_string())),
|
||||||
format!("{}", rbw_login.password),
|
format!("{}", login.password.clone().unwrap_or("".to_string())),
|
||||||
|
format!("{}", bw_item.notes.clone().unwrap_or("".to_string())),
|
||||||
)?;
|
)?;
|
||||||
if consent {
|
if consent {
|
||||||
pass::create(&vault, rbw_login.name, rbw_login.user, rbw_login.password);
|
pass::create(
|
||||||
|
&vault,
|
||||||
|
bw_item.name,
|
||||||
|
login.username.unwrap(),
|
||||||
|
login.password.unwrap_or("".to_string()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,17 +192,18 @@ fn sync() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
format!("{}", pass_login.password),
|
format!("{}", pass_login.password),
|
||||||
|
format!("{}", pass_login.notes),
|
||||||
)?;
|
)?;
|
||||||
if consent {
|
if consent {
|
||||||
pass::trash(&vault, pass_login.id);
|
pass::trash(&vault, pass_login.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
Cli::parse();
|
Cli::parse();
|
||||||
sync()?;
|
sync()?;
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-10
@@ -31,6 +31,7 @@ pub struct Item {
|
|||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct ItemContent {
|
pub struct ItemContent {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
pub note: String,
|
||||||
pub content: ItemContentContent,
|
pub content: ItemContentContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +47,8 @@ pub struct ItemLogin {
|
|||||||
pub email: String,
|
pub email: String,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
pub urls: Vec<String>,
|
||||||
|
pub totp_uri: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// our own custom structure, a counterpart to rbw::LoginItem
|
// our own custom structure, a counterpart to rbw::LoginItem
|
||||||
@@ -56,6 +59,9 @@ pub struct LoginItem {
|
|||||||
pub username: String,
|
pub username: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
pub urls: Option<Vec<String>>,
|
||||||
|
pub notes: String,
|
||||||
|
pub totp: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_pass() -> Result<(), Error> {
|
pub fn check_pass() -> Result<(), Error> {
|
||||||
@@ -75,32 +81,32 @@ pub fn check_pass() -> Result<(), Error> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vaults() -> Result<Vaults, Error> {
|
pub fn get_vaults() -> Result<Vaults, Error> {
|
||||||
let vaults_raw = sh::sh(format!("{} vault list --output json", EXECUTABLE));
|
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),
|
Ok(val) => Ok(val),
|
||||||
Err(e) => Err(Error::new(
|
Err(e) => Err(Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
format!("Couldn't get vaults: {e}"),
|
format!("Couldn't get vaults: {e}"),
|
||||||
)),
|
)),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_items(vault: &String) -> Result<Items, Error> {
|
pub fn get_items(vault: &String) -> Result<Items, Error> {
|
||||||
let items_raw = sh::sh(format!(
|
let items_raw = sh::sh(format!(
|
||||||
"{} item list '{}' --output json",
|
"{} item list '{}' --output json --show-secrets",
|
||||||
EXECUTABLE, vault
|
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),
|
Ok(val) => Ok(val),
|
||||||
Err(e) => Err(Error::new(
|
Err(e) => Err(Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
format!("Couldn't get items: {e}"),
|
format!("Couldn't get items: {e}"),
|
||||||
)),
|
)),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_logins(items: Items) -> Vec<LoginItem> {
|
pub fn get_logins(items: Items) -> Vec<LoginItem> {
|
||||||
@@ -124,6 +130,9 @@ pub fn get_logins(items: Items) -> Vec<LoginItem> {
|
|||||||
username: login.username,
|
username: login.username,
|
||||||
email: login.email,
|
email: login.email,
|
||||||
password: login.password,
|
password: login.password,
|
||||||
|
notes: item.content.note.clone(),
|
||||||
|
urls: Some(login.urls),
|
||||||
|
totp: login.totp_uri,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
@@ -135,10 +144,22 @@ pub fn update(vault: &String, item: LoginItem, user_is_actually_email: bool) {
|
|||||||
} else {
|
} else {
|
||||||
format!("usename={}", item.username)
|
format!("usename={}", item.username)
|
||||||
};
|
};
|
||||||
let output = sh::sh(format!(
|
|
||||||
"{} item update --vault-name '{}' --item-id '{}' --field 'password={}' --field '{}'",
|
let maybe_urls = if let Some(urls) = item.urls {
|
||||||
EXECUTABLE, vault, item.id, item.password, user_field_update
|
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);
|
println!("> {}", output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-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);
|
|
||||||
}
|
|
||||||
@@ -9,5 +9,5 @@ pub fn sh(command: impl Into<String>) -> String {
|
|||||||
.output()
|
.output()
|
||||||
.expect("Failed to execute command");
|
.expect("Failed to execute command");
|
||||||
|
|
||||||
return String::from_utf8(output.stdout).expect("Invalid UTF-8 sequence");
|
String::from_utf8(output.stdout).expect("Invalid UTF-8 sequence")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user