🏡 index : ~doyle/1p.git

author Jordan Doyle <jordan@doyle.la> 2020-07-06 23:26:01.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2020-07-06 23:29:54.0 +00:00:00
commit
f881ad5ea512acbce183ad83d050529fc2c752b0 [patch]
tree
e93b3a5d335548f7bc92d60bdb0a68894b741f68
parent
f20d15e13ba606a70afa0060b45b88642326c1a5
download
f881ad5ea512acbce183ad83d050529fc2c752b0.tar.gz

Add option to generate passwords



Diff

 onep-api-op/src/lib.rs |  49 +++++++++++++++++++++-
 onep-api/src/lib.rs    |   7 +++-
 onep-cli/src/main.rs   | 113 ++++++++++++++++++++++++++++++--------------------
 3 files changed, 125 insertions(+), 44 deletions(-)

diff --git a/onep-api-op/src/lib.rs b/onep-api-op/src/lib.rs
index c121b72..8dda8d6 100644
--- a/onep-api-op/src/lib.rs
+++ b/onep-api-op/src/lib.rs
@@ -2,6 +2,7 @@

use serde::Deserialize;
use serde_json::Value;
use std::borrow::Cow;
use std::process::Command;

#[derive(thiserror::Error, Debug)]
@@ -188,9 +189,20 @@ impl Into<onep_api::ItemField> for GetItemSectionField {
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CreateItem {
    uuid: String,
    vault_uuid: String,
}

pub struct OnepasswordOp {}

fn exec(args: &[&str]) -> Result<Vec<u8>, Error> {
fn exec<I, S>(args: I) -> Result<Vec<u8>, Error>
where
    I: IntoIterator<Item = S>,
    S: AsRef<std::ffi::OsStr>,
{
    let cmd = Command::new("op")
        .args(args)
        .output()
@@ -251,4 +263,39 @@ impl onep_api::OnePassword for OnepasswordOp {

        Ok(Some(ret.into()))
    }

    fn generate(
        &self,
        name: &str,
        username: Option<&str>,
        url: Option<&str>,
        tags: Option<&str>,
    ) -> Result<onep_api::Item, Self::Error> {
        let mut args = Vec::with_capacity(12);

        args.push(Cow::Borrowed("create"));
        args.push(Cow::Borrowed("item"));
        args.push(Cow::Borrowed("Login"));
        args.push(Cow::Borrowed("--generate-password"));
        args.push(Cow::Borrowed("--title"));
        args.push(Cow::Borrowed(name));

        if let Some(url) = url {
            args.push(Cow::Borrowed("--url"));
            args.push(Cow::Borrowed(url));
        }

        if let Some(tags) = tags {
            args.push(Cow::Borrowed("--tags"));
            args.push(Cow::Borrowed(tags));
        }

        if let Some(username) = username {
            args.push(Cow::Owned(format!("username={}", username)));
        }

        let ret: CreateItem = serde_json::from_slice(&exec(args.iter().map(Cow::as_ref))?)?;

        Ok(self.get(&ret.uuid)?.unwrap_or_else(|| unreachable!()))
    }
}
diff --git a/onep-api/src/lib.rs b/onep-api/src/lib.rs
index 55f78d0..549fa50 100644
--- a/onep-api/src/lib.rs
+++ b/onep-api/src/lib.rs
@@ -48,4 +48,11 @@ pub trait OnePassword {
    fn vaults(&self) -> Result<Vec<VaultMetadata>, Self::Error>;
    fn search(&self, terms: Option<&str>) -> Result<Vec<ItemMetadata>, Self::Error>;
    fn get(&self, uuid: &str) -> Result<Option<Item>, Self::Error>;
    fn generate(
        &self,
        name: &str,
        username: Option<&str>,
        url: Option<&str>,
        tags: Option<&str>,
    ) -> Result<Item, Self::Error>;
}
diff --git a/onep-cli/src/main.rs b/onep-cli/src/main.rs
index 9840657..a0e07ed 100644
--- a/onep-cli/src/main.rs
+++ b/onep-cli/src/main.rs
@@ -21,8 +21,8 @@ enum Error {
/// 1password cli for humans
enum Opt {
    /// List all items
    #[clap(alias = "list")]
    Ls {
    #[clap(alias = "ls")]
    List {
        #[clap(long, short = 'i')]
        show_uuids: bool,
        #[clap(long, short = 'n')]
@@ -35,6 +35,21 @@ enum Opt {
    /// Show existing password and optionally put it on the clipboard
    #[clap(alias = "get")]
    Show { uuid: String },
    /// Generates a new password and stores it in your password store
    #[clap(alias = "gen")]
    Generate {
        /// Name of the login to create
        name: String,
        /// Username to associate with the login
        #[clap(long, short = 'n')]
        username: Option<String>,
        /// URL to associate with the login
        #[clap(long, short = 'u')]
        url: Option<String>,
        /// Comma-separated list of tags to associate with the login
        #[clap(long, short = 't')]
        tags: Option<String>,
    },
}

fn main() {
@@ -44,14 +59,13 @@ fn main() {
    }
}

#[warn(clippy::too_many_lines)]
#[allow(clippy::non_ascii_literal)]
fn run<T: OnePassword>(imp: &T) -> anyhow::Result<()>
where
    T::Error: 'static + std::error::Error + Send + Sync,
{
    match Opt::parse() {
        Opt::Ls {
        Opt::List {
            show_uuids,
            show_account_names,
        } => {
@@ -136,52 +150,65 @@ where
        }
        Opt::Show { uuid } => {
            let result = imp.get(&uuid)?.ok_or(Error::NotFound)?;
            show(result);
        }
        Opt::Generate {
            name,
            username,
            url,
            tags,
        } => {
            let result =
                imp.generate(&name, username.as_deref(), url.as_deref(), tags.as_deref())?;
            show(result);
        }
    }

            let mut table = Table::new();
            table.style = TableStyle::extended();

            table.add_row(Row::new(vec![TableCell::new_with_alignment(
                result.title,
                2,
                Alignment::Center,
            )]));

            for field in result.fields {
                table.add_row(Row::new(vec![
                    TableCell::new(field.name),
                    TableCell::new_with_alignment(field.value, 1, Alignment::Right),
                ]));
            }
    Ok(())
}

            println!("{}", table.render());
fn show(item: onep_api::Item) {
    let mut table = Table::new();
    table.style = TableStyle::extended();

    table.add_row(Row::new(vec![TableCell::new_with_alignment(
        item.title,
        2,
        Alignment::Center,
    )]));

    for field in item.fields {
        table.add_row(Row::new(vec![
            TableCell::new(field.name),
            TableCell::new_with_alignment(field.value, 1, Alignment::Right),
        ]));
    }

            for section in result.sections {
                if section.fields.is_empty() {
                    continue;
                }
    println!("{}", table.render());

                let mut table = Table::new();
                table.style = TableStyle::extended();
    for section in item.sections {
        if section.fields.is_empty() {
            continue;
        }

                if !section.name.is_empty() {
                    table.add_row(Row::new(vec![TableCell::new_with_alignment(
                        section.name,
                        2,
                        Alignment::Center,
                    )]));
                }
        let mut table = Table::new();
        table.style = TableStyle::extended();

                for field in section.fields {
                    table.add_row(Row::new(vec![
                        TableCell::new(field.name),
                        TableCell::new_with_alignment(field.value, 1, Alignment::Right),
                    ]));
                }
        if !section.name.is_empty() {
            table.add_row(Row::new(vec![TableCell::new_with_alignment(
                section.name,
                2,
                Alignment::Center,
            )]));
        }

                println!("{}", table.render());
            }
        for field in section.fields {
            table.add_row(Row::new(vec![
                TableCell::new(field.name),
                TableCell::new_with_alignment(field.value, 1, Alignment::Right),
            ]));
        }
    }

    Ok(())
        println!("{}", table.render());
    }
}