From 3a83840f15224ecd0c381743291a19bcb06c867d Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 10 Jul 2022 12:14:32 +0200 Subject: [PATCH] cli: Support for `pass(1)` as credential storage `pass(1)`[1] is a small CLI-based password manager. When passing `--pass-path edu/kit/uXXXX` to `KIT-ILIAS-Downloader`, it now attempts to retrieve the password from `pass(1)`. It is assumed that `pass(1)` is available in the `$PATH` of the process. If that's not the case, it errors out with an error like this: Error: credentials input failed Caused by: pass not found in $PATH! It's also taken care of the case where the value `--pass-path` is not present in the store, the error will look like this: Error: credentials input failed Caused by: `pass` failed with non-zero exit code 1: Error: edu/kit/uXXXX is not in the password store. Closes #32 [1] https://www.passwordstore.org/ --- src/cli.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index 2ff19d3..8152eb3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later +use std::io::{Error, ErrorKind}; use std::path::PathBuf; +use std::process::Command; use std::sync::atomic::{AtomicBool, AtomicUsize}; use anyhow::anyhow; @@ -72,6 +74,10 @@ pub struct Opt { #[structopt(short = "P", long)] pub password: Option, + /// Path inside `pass(1)` to the password for shibboleth. + #[structopt(long)] + pub pass_path: Option, + /// ILIAS page to download #[structopt(long)] pub sync_url: Option, @@ -170,6 +176,35 @@ pub fn ask_user_pass(opt: &Opt) -> Result<(String, String)> { should_store = true; } } + } else if let Some(pass_path) = &opt.pass_path { + let pw_out = Command::new("pass") + .arg("show") + .arg(pass_path) + .output() + .map_err(|x| { + if x.kind() == ErrorKind::NotFound { + Error::new(ErrorKind::NotFound, "pass not found in $PATH!") + } else { + x + } + })?; + if !pw_out.status.success() { + return Err(Error::new( + ErrorKind::Other, + format!( + "`pass` failed with non-zero exit code {}: {}", + pw_out.status.code() + .expect("Failed retrieving pass exit code!"), + String::from_utf8(pw_out.stderr) + .expect("Failed decoding stderr of pass!") + ) + ))? + } else { + pass = String::from_utf8(pw_out.stdout).map(|x| { + x.trim_end().to_string() + }).expect("utf-8 decode of `pass(1)`-output failed"); + should_store = false; + } } else { pass = rpassword::prompt_password("Password: ").context("password prompt")?; should_store = true;