Skip to content

Commit

Permalink
Export bookmarks with proper account uuid (instead of user uuid)
Browse files Browse the repository at this point in the history
  • Loading branch information
dteare committed Mar 9, 2022
1 parent 2b6451e commit b90d986
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 53 deletions.
25 changes: 14 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod op;
mod op7_metadata;

use op::{find_accounts, find_items, find_vaults, Account, Item, Vault};
use op::{find_items, find_vaults, load_all_accounts, AccountDetails, ItemOverview, VaultOverview};
use op7_metadata::write_items;

use clap::Parser;
Expand All @@ -27,9 +27,12 @@ struct Cli {
fn main() {
let args = Cli::parse();
if args.accounts.len() == 0 {
println!("Generating metadata for all accounts...");
println!("Will create bookmark metadata for all accounts...");
} else {
println!("Generating metadata for {:?}", args.accounts);
println!(
"Will create bookmark metadata for account user uuids {:?}...",
args.accounts
);
}

generate_opbookmarks(&args.accounts, &args.export_path);
Expand All @@ -44,28 +47,28 @@ fn main() {
}

fn generate_opbookmarks(account_user_uuids: &Vec<String>, export_path: &std::path::PathBuf) {
let accounts = find_accounts(account_user_uuids);
let accounts = load_all_accounts(account_user_uuids);

if let Err(err) = accounts {
eprintln!("Failed to load accounts: {:?}", err);
exit(1);
}

let accounts = accounts.unwrap();
let mut vaults_by_account: HashMap<Account, Vec<Vault>> = HashMap::new();
let mut items_by_vault: HashMap<Vault, Vec<Item>> = HashMap::new();
let mut vaults_by_account: HashMap<AccountDetails, Vec<VaultOverview>> = HashMap::new();
let mut items_by_vault: HashMap<VaultOverview, Vec<ItemOverview>> = HashMap::new();

println!(
"Exporting bookmarks for accounts {:?}",
accounts
.iter()
.map(|a| a.user_uuid.clone())
.map(|a| a.id.clone())
.collect::<Vec<String>>()
);

// Collect the vaults for each account
for account in accounts.iter() {
let vaults = find_vaults(account);
let vaults = find_vaults(&account.id);

match vaults {
Ok(vaults) => {
Expand All @@ -74,7 +77,7 @@ fn generate_opbookmarks(account_user_uuids: &Vec<String>, export_path: &std::pat
Err(err) => {
eprintln!(
"Failed to load vaults for account {}: {:?}",
account.user_uuid, err
account.id, err
);
}
}
Expand All @@ -83,7 +86,7 @@ fn generate_opbookmarks(account_user_uuids: &Vec<String>, export_path: &std::pat
// Collect the items for each vault
for (account, vaults) in vaults_by_account.iter() {
for vault in vaults.iter() {
let items = find_items(account, vault);
let items = find_items(&account.id, &vault.id);

match items {
Ok(items) => {
Expand All @@ -92,7 +95,7 @@ fn generate_opbookmarks(account_user_uuids: &Vec<String>, export_path: &std::pat
Err(err) => {
eprintln!(
"Failed to load items for vault {} in account {}: {:?}",
vault.id, account.user_uuid, err
vault.id, account.id, err
)
}
}
Expand Down
98 changes: 64 additions & 34 deletions src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ use serde::{Deserialize, Serialize};
use std::process::Command;

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Account {
pub struct AccountOverview {
pub email: String,
pub url: String,
pub user_uuid: String,
}

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct AccountDetails {
id: String,
name: String,
domain: String,
pub id: String,
pub name: String,
pub domain: String,

#[serde(rename = "type")]
account_type: String,
state: String,
created_at: String,
pub account_type: String,
pub state: String,
pub created_at: String,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Vault {
pub struct VaultOverview {
pub id: String,
pub name: Option<String>,
}
Expand All @@ -43,11 +43,11 @@ pub struct VaultDetails {
}

#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Item {
pub struct ItemOverview {
pub id: String,
pub title: String,
pub version: usize,
pub vault: Vault,
pub vault: VaultOverview,
pub category: String,
pub last_edited_by: String,
pub created_at: String,
Expand All @@ -56,22 +56,22 @@ pub struct Item {

#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ItemDetails {
id: String,
title: String,
tags: Vec<String>,
version: usize,
vault: Vault,
category: String,
last_edited_by: String,
created_at: String,
updated_at: String,
urls: Vec<OPURL>,
pub id: String,
pub title: String,
pub tags: Vec<String>,
pub version: usize,
pub vault: VaultOverview,
pub category: String,
pub last_edited_by: String,
pub created_at: String,
pub updated_at: String,
pub urls: Vec<OPURL>,
}

#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct OPURL {
primary: bool,
href: String,
pub primary: bool,
pub href: String,
}

#[derive(Debug)]
Expand All @@ -81,7 +81,34 @@ pub enum Error {
// Serialize(serde_json::Error),
}

pub fn find_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<Account>, Error> {
pub fn load_all_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<AccountDetails>, Error> {
let accounts = find_accounts(account_user_uuids);

match accounts {
Ok(accounts) => {
let mut details: Vec<AccountDetails> = vec![];
for account in accounts.iter() {
let ad = get_account(&account.user_uuid);

match ad {
Ok(ad) => details.push(ad),
Err(e) => {
eprint!("Error loading account details: {:?}", e);
return Err(Error::OPCLI(format!(
"Failed to load details for account {}",
account.user_uuid
)));
}
}
}

Ok(details)
}
Err(e) => Err(e),
}
}

pub fn find_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<AccountOverview>, Error> {
let output = Command::new("op")
.arg("--format")
.arg("json")
Expand All @@ -98,7 +125,7 @@ pub fn find_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<Account>, E
));
}

let accounts: Result<Vec<Account>, Error> =
let accounts: Result<Vec<AccountOverview>, Error> =
serde_json::from_slice(json.as_slice()).map_err(|e| Error::Deserialize(e));

match accounts {
Expand All @@ -111,7 +138,7 @@ pub fn find_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<Account>, E
Ok(accounts)
} else {
// Limit to the specified accounts
let mut specified_accounts: Vec<Account> = vec![];
let mut specified_accounts: Vec<AccountOverview> = vec![];
for uuid in account_user_uuids.iter() {
match accounts.iter().find(|a| (*a).user_uuid == uuid.as_str()) {
Some(account) => {
Expand All @@ -133,7 +160,7 @@ pub fn find_accounts(account_user_uuids: &Vec<String>) -> Result<Vec<Account>, E
}

// op --account BXRGOJ2Z5JB4RMA7FUYUURELUE --format json account get
pub fn get_account(user_id: String) -> Result<AccountDetails, Error> {
pub fn get_account(user_id: &String) -> Result<AccountDetails, Error> {
let output = Command::new("op")
.arg("--account")
.arg(user_id)
Expand All @@ -155,13 +182,12 @@ pub fn get_account(user_id: String) -> Result<AccountDetails, Error> {
serde_json::from_slice(json.as_slice()).map_err(|e| Error::Deserialize(e))
}

pub fn find_vaults(account: &Account) -> Result<Vec<Vault>, Error> {
println!("account={:?}", account);
pub fn find_vaults(account_id: &String) -> Result<Vec<VaultOverview>, Error> {
let output = Command::new("op")
.arg("--format")
.arg("json")
.arg("--account")
.arg(account.user_uuid.clone())
.arg(account_id)
.arg("vault")
.arg("list")
.output()
Expand All @@ -179,7 +205,7 @@ pub fn find_vaults(account: &Account) -> Result<Vec<Vault>, Error> {
}

// op --account BXRGOJ2Z5JB4RMA7FUYUURELUE --format json vault get jnnjfdrzr5rawkimmsvp3zzzxe
pub fn get_vault(account: &Account, vault_id: String) -> Result<VaultDetails, Error> {
pub fn get_vault(account: &AccountOverview, vault_id: String) -> Result<VaultDetails, Error> {
let output = Command::new("op")
.arg("--format")
.arg("json")
Expand All @@ -202,16 +228,16 @@ pub fn get_vault(account: &Account, vault_id: String) -> Result<VaultDetails, Er
serde_json::from_slice(json.as_slice()).map_err(|e| Error::Deserialize(e))
}

pub fn find_items(account: &Account, vault: &Vault) -> Result<Vec<Item>, Error> {
pub fn find_items(account_id: &String, vault_id: &String) -> Result<Vec<ItemOverview>, Error> {
let output = Command::new("op")
.arg("--format")
.arg("json")
.arg("--account")
.arg(account.url.clone())
.arg(account_id)
.arg("item")
.arg("list")
.arg("--vault")
.arg(vault.id.clone())
.arg(vault_id)
.output()
.expect("failed to execute `op` command");
let json = output.stdout;
Expand All @@ -227,7 +253,11 @@ pub fn find_items(account: &Account, vault: &Vault) -> Result<Vec<Item>, Error>
}

// op --account BXRGOJ2Z5JB4RMA7FUYUURELUE --vault jnnjfdrzr5rawkimmsvp3zzzxe --format json item get fu5rgmahfihx4j6lludeyx3oei
pub fn get_item(account: &Account, vault: &Vault, item_id: String) -> Result<ItemDetails, Error> {
pub fn get_item(
account: &AccountOverview,
vault: &VaultOverview,
item_id: String,
) -> Result<ItemDetails, Error> {
let output = Command::new("op")
.arg("--account")
.arg(account.url.clone())
Expand Down
20 changes: 12 additions & 8 deletions src/op7_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Create metadata files that conform to the format used by 1Password 7
use crate::op::{Account, Item, Vault};
use crate::op::{AccountDetails, ItemOverview, VaultOverview};
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
Expand Down Expand Up @@ -45,15 +45,15 @@ pub struct OP7ItemMetaData {

pub fn write_items(
export_path: &std::path::PathBuf,
items: &Vec<Item>,
vault: &Vault,
account: &Account,
items: &Vec<ItemOverview>,
vault: &VaultOverview,
account: &AccountDetails,
) {
let mut path = export_path.clone();
path.push(account.user_uuid.clone());
path.push(account.id.clone());

for item in items.iter() {
let op7_item = create_op7_metadata(&item, &vault, &account);
let op7_item = create_op7_metadata(&item, &vault, &account.id);

match serde_json::to_string(&op7_item) {
Ok(json) => {
Expand Down Expand Up @@ -96,15 +96,19 @@ fn write_file(path: std::path::PathBuf, contents: String) {
}
}

fn create_op7_metadata(item: &Item, vault: &Vault, account: &Account) -> OP7ItemMetaData {
fn create_op7_metadata(
item: &ItemOverview,
vault: &VaultOverview,
account_id: &String,
) -> OP7ItemMetaData {
return OP7ItemMetaData {
uuid: item.id.clone(),
item_description: format!("Login from {}", &vault.name.clone().unwrap()),
item_title: item.title.clone(),
vault_name: vault.name.clone().unwrap().clone(),
vault_uuid: vault.id.clone(),
category_plural_name: item.category.clone(), // TODO: Map SECURE_NOTE, etc
profile_uuid: account.user_uuid.clone(),
profile_uuid: account_id.clone(),
website_urls: vec![],
category_singular_name: item.category.clone(),
category_uuid: "001".to_string(),
Expand Down

0 comments on commit b90d986

Please sign in to comment.