Propagate errors up out of The 1Password command-line tool provides commands to manage and administer a 1Password account.
Sign in to an account to get started. Run `op signin --help` to learn
more.
HOW TO SPECIFY OBJECTS
You can specify all objects by name or UUID. You can also specify some
objects by other attributes:
* Items: item link
* Login or Password items: domain name
* Users: email address
When you specify an item by name or domain, there may be more than one
item that matches. To be more specific, use the `--vault` option to
only look in one vault at a time, or use a unique ID (UUID) instead.
Usage:
op [command]
Available Commands:
add Grant access to groups or vaults
confirm Confirm a user
create Create an object
delete Remove an object
edit Edit an object
encode Encode the JSON needed to create an item
forget Remove a 1Password account from this device
get Get details about an object
help Get help for a command.
list List objects and events
reactivate Reactivate a suspended user
remove Revoke access to groups or vaults
signin Sign in to a 1Password account
signout Sign out of a 1Password account
suspend Suspend a user
update Check for updates
Flags:
--account shorthand use the account with this shorthand
-h, --help get help for op
--session token authenticate with this session token
--version version for op
Use "op [command] --help" for more information about a command.
Diff
Cargo.lock | 30 ++++++++++++++++++++++++++++++
onep-api/Cargo.toml | 1 +
onep-cli/Cargo.toml | 4 +++-
onep-api-op/src/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
onep-api/src/lib.rs | 20 ++++++++++++++++----
onep-cli/src/main.rs | 21 ++++++++++++++++-----
6 files changed, 101 insertions(+), 77 deletions(-)
@@ -9,6 +9,11 @@
]
[[package]]
name = "anyhow"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -170,6 +175,9 @@
[[package]]
name = "onep-api"
version = "0.1.0"
dependencies = [
"thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "onep-api-op"
@@ -185,6 +193,7 @@
name = "onep-cli"
version = "0.1.0"
dependencies = [
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 3.0.0-beta.1 (git+https://github.com/clap-rs/clap)",
"colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -361,6 +370,24 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -425,6 +452,7 @@
[metadata]
"checksum aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)" = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
@@ -465,6 +493,8 @@
"checksum term-table 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dea05ef801340799f4c96296e9e8cf79cdfa66ef66fe991522591fde77405105"
"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
"checksum textwrap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
"checksum thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
"checksum thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
@@ -7,3 +7,4 @@
[dependencies]
thiserror = "1.0"
@@ -13,4 +13,6 @@
clap = { git = "https://github.com/clap-rs/clap" }
term-table = "1.3"
itertools = "0.9"
colored = "1.9"
colored = "1.9"
anyhow = "1.0"
@@ -1,5 +1,6 @@
use serde::Deserialize;
use serde_json::Value;
use onep_api::OnePasswordApiError;
use std::process::Command;
#[derive(Debug, Deserialize)]
@@ -118,69 +119,49 @@
pub struct OnepasswordOp {}
impl OnepasswordOp {
fn exec(&self, args: &[&str]) -> Result<Vec<u8>, OnePasswordApiError> {
let cmd = Command::new("op").args(args).output().map_err(OnePasswordApiError::Exec)?;
if cmd.status.success() {
Ok(cmd.stdout)
} else {
Err(OnePasswordApiError::Backend(
std::str::from_utf8(&cmd.stderr).unwrap().to_string()
))
}
}
}
impl onep_api::OnePassword for OnepasswordOp {
fn totp(&self, uuid: &str) -> String {
std::str::from_utf8(
&Command::new("op")
.arg("get")
.arg("totp")
.arg(uuid)
.output()
.expect("failed to exec get totp")
.stdout,
)
.expect("failed to parse get totp output as utf8")
.to_string()
fn totp(&self, uuid: &str) -> Result<String, OnePasswordApiError> {
Ok(std::str::from_utf8(&self.exec(&["get", "totp", uuid])?).unwrap().to_string())
}
fn account(&self) -> Result<onep_api::AccountMetadata, OnePasswordApiError> {
let ret: GetAccount = serde_json::from_slice(&self.exec(&["get", "account"])?).unwrap();
fn account(&self) -> onep_api::AccountMetadata {
let ret: GetAccount = serde_json::from_slice(
&Command::new("op")
.arg("get")
.arg("account")
.output()
.expect("failed to exec get account")
.stdout,
)
.expect("failed to parse json");
onep_api::AccountMetadata {
Ok(onep_api::AccountMetadata {
name: ret.name,
domain: ret.domain,
}
})
}
fn vaults(&self) -> Result<Vec<onep_api::VaultMetadata>, OnePasswordApiError> {
let ret: Vec<ListVault> = serde_json::from_slice(&self.exec(&["list", "vaults"])?).unwrap();
fn vaults(&self) -> Vec<onep_api::VaultMetadata> {
let ret: Vec<ListVault> = serde_json::from_slice(
&Command::new("op")
.arg("list")
.arg("vaults")
.output()
.expect("failed to exec list vaults")
.stdout,
)
.expect("failed to parse json");
ret.into_iter()
Ok(ret.into_iter()
.map(|v| onep_api::VaultMetadata {
uuid: v.uuid,
name: v.name,
})
.collect()
.collect())
}
fn search(&self, terms: Option<&str>) -> Vec<onep_api::ItemMetadata> {
let ret: Vec<ListItem> = serde_json::from_slice(
&Command::new("op")
.arg("list")
.arg("items")
.output()
.expect("failed to exec list items")
.stdout,
)
.expect("failed to parse json");
ret.into_iter()
fn search(&self, terms: Option<&str>) -> Result<Vec<onep_api::ItemMetadata>, OnePasswordApiError> {
let ret: Vec<ListItem> = serde_json::from_slice(&self.exec(&["list", "items"])?).unwrap();
Ok(ret.into_iter()
.filter(|v| {
if let Some(terms) = terms {
v.uuid == terms
@@ -199,22 +180,13 @@
uuid: v.uuid,
vault_uuid: v.vault_uuid,
})
.collect()
.collect())
}
fn get(&self, uuid: &str) -> Option<onep_api::Item> {
let ret: GetItem = serde_json::from_slice(
&Command::new("op")
.arg("get")
.arg("item")
.arg(uuid)
.output()
.expect("failed to exec get item")
.stdout,
)
.expect("failed to parse json");
Some(onep_api::Item {
fn get(&self, uuid: &str) -> Result<Option<onep_api::Item>, OnePasswordApiError> {
let ret: GetItem = serde_json::from_slice(&self.exec(&["get", "item", uuid])?).unwrap();
Ok(Some(onep_api::Item {
title: ret.overview.title,
fields: ret
.details
@@ -237,6 +209,6 @@
.collect(),
})
.collect(),
})
}))
}
}
@@ -1,3 +1,13 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum OnePasswordApiError {
#[error("1password backend returned an error:\n{0}")]
Backend(String),
#[error("failed to exec backend:\n{0}")]
Exec(std::io::Error),
}
#[derive(Debug)]
pub struct AccountMetadata {
pub name: String,
@@ -38,9 +48,9 @@
}
pub trait OnePassword {
fn totp(&self, uuid: &str) -> String;
fn account(&self) -> AccountMetadata;
fn vaults(&self) -> Vec<VaultMetadata>;
fn search(&self, terms: Option<&str>) -> Vec<ItemMetadata>;
fn get(&self, uuid: &str) -> Option<Item>;
fn totp(&self, uuid: &str) -> Result<String, OnePasswordApiError>;
fn account(&self) -> Result<AccountMetadata, OnePasswordApiError>;
fn vaults(&self) -> Result<Vec<VaultMetadata>, OnePasswordApiError>;
fn search(&self, terms: Option<&str>) -> Result<Vec<ItemMetadata>, OnePasswordApiError>;
fn get(&self, uuid: &str) -> Result<Option<Item>, OnePasswordApiError>;
}
@@ -30,6 +30,13 @@
}
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
std::process::exit(1);
}
}
fn run() -> anyhow::Result<()> {
let imp = onep_api_op::OnepasswordOp {};
match Opt::parse() {
@@ -37,9 +44,9 @@
show_uuids,
show_account_names,
} => {
let account = imp.account();
let vaults = imp.vaults();
let results = imp.search(None);
let account = imp.account()?;
let vaults = imp.vaults()?;
let results = imp.search(None)?;
let mut results_grouped: Vec<(_, Vec<_>)> = Vec::new();
for (key, group) in &results.into_iter().group_by(|v| v.vault_uuid.clone()) {
@@ -108,9 +115,9 @@
}
}
}
Opt::Totp { uuid } => println!("{}", imp.totp(&uuid).trim()),
Opt::Totp { uuid } => println!("{}", imp.totp(&uuid)?.trim()),
Opt::Search { terms } => {
for result in imp.search(Some(&terms)) {
for result in imp.search(Some(&terms))? {
println!("[{}]", result.title.green());
println!("{}", result.account_info);
println!("{}", result.uuid);
@@ -118,7 +125,7 @@
}
}
Opt::Show { uuid } => {
let result = imp.get(&uuid).unwrap();
let result = imp.get(&uuid)?.unwrap();
let mut table = Table::new();
table.style = TableStyle::extended();
@@ -165,4 +172,6 @@
}
}
}
Ok(())
}