🏡 index : ~doyle/1p.git

author Jordan Doyle <jordan@doyle.la> 2020-07-05 17:00:07.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2020-07-05 17:00:07.0 +01:00:00
commit
887a2efe4f9534593c87682e2e869a816309ef8e [patch]
tree
15b73add1868ce963cb00e762b1061a8a5942131
parent
9291f942d00a0a2aa2f3ab9e6ed4ecf6da2de314
download
887a2efe4f9534593c87682e2e869a816309ef8e.tar.gz

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(-)

diff --git a/Cargo.lock b/Cargo.lock
index 9b57655..5a20de1 100644
--- a/Cargo.lock
+++ a/Cargo.lock
@@ -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"
diff --git a/onep-api/Cargo.toml b/onep-api/Cargo.toml
index d45ed62..57b5b99 100644
--- a/onep-api/Cargo.toml
+++ a/onep-api/Cargo.toml
@@ -7,3 +7,4 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
thiserror = "1.0"
diff --git a/onep-cli/Cargo.toml b/onep-cli/Cargo.toml
index 61e6441..581178b 100644
--- a/onep-cli/Cargo.toml
+++ a/onep-cli/Cargo.toml
@@ -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"
diff --git a/onep-api-op/src/lib.rs b/onep-api-op/src/lib.rs
index 53f0feb..1e2e7fa 100644
--- a/onep-api-op/src/lib.rs
+++ a/onep-api-op/src/lib.rs
@@ -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(),
        })
        }))
    }
}
diff --git a/onep-api/src/lib.rs b/onep-api/src/lib.rs
index 28e8069..fd6523c 100644
--- a/onep-api/src/lib.rs
+++ a/onep-api/src/lib.rs
@@ -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>;
}
diff --git a/onep-cli/src/main.rs b/onep-cli/src/main.rs
index 454473b..51ff0b0 100644
--- a/onep-cli/src/main.rs
+++ a/onep-cli/src/main.rs
@@ -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(())
}