summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2020-09-08 18:23:30 +0200
committerDaniel Mueller <deso@posteo.net>2021-01-10 17:37:57 -0800
commit9593dfd03a6ca085d649ca090b6ec5e5f0104e78 (patch)
treecef944516af5c92561c2610f9c8a21776e95cac9 /src
parent9975f8810399d5e535f2e2fd62b4dc56b313f955 (diff)
downloadnitrocli-9593dfd03a6ca085d649ca090b6ec5e5f0104e78.tar.gz
nitrocli-9593dfd03a6ca085d649ca090b6ec5e5f0104e78.tar.bz2
Add --usb-path option to select device
This patch adds the --usb-path option as an additional way to filter the Nitrokey device to connect to. While the serial number is a better identifier in theory, the Nitrokey Storage devices do not send their serial number in the USB device descriptor. Having the --usb-path options allows users to select one of multiple Nitrokey Storage devices. While we could directly call the nitrokey::Manager::connect_path function with the specified path, we integrate the --usb-path option into the existing find_device function for consistent error messages and to avoid having to duplicate the --model and --serial-number checks.
Diffstat (limited to 'src')
-rw-r--r--src/args.rs3
-rw-r--r--src/commands.rs10
-rw-r--r--src/config.rs5
-rw-r--r--src/tests/run.rs92
4 files changed, 105 insertions, 5 deletions
diff --git a/src/args.rs b/src/args.rs
index 0d77806..80abe17 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -23,6 +23,9 @@ pub struct Args {
number_of_values = 1
)]
pub serial_numbers: Vec<nitrokey::SerialNumber>,
+ /// Sets the USB path of the device to connect to
+ #[structopt(long, global = true)]
+ pub usb_path: Option<String>,
/// Disables the cache for all secrets.
#[structopt(long, global = true)]
pub no_cache: bool,
diff --git a/src/commands.rs b/src/commands.rs
index 455ff4d..d352ca2 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -55,6 +55,9 @@ fn format_filter(config: &config::Config) -> String {
.collect::<Vec<_>>();
filters.push(format!("serial number in [{}]", serial_numbers.join(", ")));
}
+ if let Some(path) = &config.usb_path {
+ filters.push(format!("usb path={}", path));
+ }
if filters.is_empty() {
String::new()
} else {
@@ -75,7 +78,8 @@ fn find_device(config: &config::Config) -> anyhow::Result<nitrokey::DeviceInfo>
.serial_number
.map(|sn| config.serial_numbers.contains(&sn))
.unwrap_or_default()
- });
+ })
+ .filter(|device| config.usb_path.is_none() || config.usb_path.as_ref() == Some(&device.path));
let device = iter
.next()
@@ -83,8 +87,8 @@ fn find_device(config: &config::Config) -> anyhow::Result<nitrokey::DeviceInfo>
anyhow::ensure!(
iter.next().is_none(),
- "Multiple Nitrokey devices found{}. Use the --model and --serial-number options to \
- select one",
+ "Multiple Nitrokey devices found{}. Use the --model, --serial-number, and --usb-path options \
+ to select one",
format_filter(config)
);
Ok(device)
diff --git a/src/config.rs b/src/config.rs
index a609189..6bb6efb 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -32,6 +32,8 @@ pub struct Config {
#[merge(strategy = merge::vec::overwrite_empty)]
#[serde(default, deserialize_with = "deserialize_serial_number_vec")]
pub serial_numbers: Vec<nitrokey::SerialNumber>,
+ /// The USB path of the device to connect to.
+ pub usb_path: Option<String>,
/// Whether to bypass the cache for all secrets or not.
#[merge(strategy = merge::bool::overwrite_false)]
#[serde(default)]
@@ -74,6 +76,9 @@ impl Config {
if !args.serial_numbers.is_empty() {
self.serial_numbers = args.serial_numbers.clone();
}
+ if args.usb_path.is_some() {
+ self.usb_path = args.usb_path.clone();
+ }
if args.no_cache {
self.no_cache = true;
}
diff --git a/src/tests/run.rs b/src/tests/run.rs
index 4f53608..b39b1da 100644
--- a/src/tests/run.rs
+++ b/src/tests/run.rs
@@ -4,6 +4,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use std::collections;
+use std::ops;
use std::path;
use super::*;
@@ -116,7 +117,7 @@ fn connect_multiple(_model: nitrokey::Model) -> anyhow::Result<()> {
let err = res.unwrap_err().to_string();
assert_eq!(
err,
- "Multiple Nitrokey devices found. Use the --model and --serial-number options to select one"
+ "Multiple Nitrokey devices found. Use the --model, --serial-number, and --usb-path options to select one"
);
}
Ok(())
@@ -143,6 +144,32 @@ fn connect_wrong_serial_number(_model: nitrokey::Model) {
}
#[test_device]
+fn connect_usb_path(_model: nitrokey::Model) -> anyhow::Result<()> {
+ for device in nitrokey::list_devices()? {
+ let res = Nitrocli::new().handle(&["status", &format!("--usb-path={}", device.path)]);
+ assert!(res.is_ok());
+ let res = res?;
+ if let Some(model) = device.model {
+ assert!(res.contains(&format!("model: {}\n", model)));
+ }
+ if let Some(sn) = device.serial_number {
+ assert!(res.contains(&format!("serial number: {}\n", sn)));
+ }
+ }
+ Ok(())
+}
+
+#[test_device]
+fn connect_wrong_usb_path(_model: nitrokey::Model) {
+ let res = Nitrocli::new().handle(&["status", "--usb-path=not-a-path"]);
+ let err = res.unwrap_err().to_string();
+ assert_eq!(
+ err,
+ "Nitrokey device not found (filter: usb path=not-a-path)"
+ );
+}
+
+#[test_device]
fn connect_model(_model: nitrokey::Model) -> anyhow::Result<()> {
let devices = nitrokey::list_devices()?;
let mut model_counts = collections::BTreeMap::new();
@@ -172,10 +199,71 @@ fn connect_model(_model: nitrokey::Model) -> anyhow::Result<()> {
format!(
"Multiple Nitrokey devices found (filter: model={}). ",
model.to_lowercase()
- ) + "Use the --model and --serial-number options to select one"
+ ) + "Use the --model, --serial-number, and --usb-path options to select one"
);
}
}
Ok(())
}
+
+#[test_device]
+fn connect_usb_path_model_serial(_model: nitrokey::Model) -> anyhow::Result<()> {
+ let devices = nitrokey::list_devices()?;
+ for device in devices {
+ let mut args = Vec::new();
+ args.push("status".to_owned());
+ args.push(format!("--usb-path={}", device.path));
+ if let Some(model) = device.model {
+ args.push(format!("--model={}", model.to_string().to_lowercase()));
+ }
+ if let Some(sn) = device.serial_number {
+ args.push(format!("--serial-number={}", sn));
+ }
+
+ let res = Nitrocli::new().handle(&args.iter().map(ops::Deref::deref).collect::<Vec<_>>())?;
+ if let Some(model) = device.model {
+ assert!(res.contains(&format!("model: {}\n", model)));
+ }
+ if let Some(sn) = device.serial_number {
+ assert!(res.contains(&format!("serial number: {}\n", sn)));
+ }
+ }
+ Ok(())
+}
+
+#[test_device]
+fn connect_usb_path_model_wrong_serial(_model: nitrokey::Model) -> anyhow::Result<()> {
+ let devices = nitrokey::list_devices()?;
+ for device in devices {
+ let mut args = Vec::new();
+ args.push("status".to_owned());
+ args.push(format!("--usb-path={}", device.path));
+ if let Some(model) = device.model {
+ args.push(format!("--model={}", model.to_string().to_lowercase()));
+ }
+ args.push("--serial-number=0xdeadbeef".to_owned());
+
+ let res = Nitrocli::new().handle(&args.iter().map(ops::Deref::deref).collect::<Vec<_>>());
+ let err = res.unwrap_err().to_string();
+ if let Some(model) = device.model {
+ assert_eq!(
+ err,
+ format!(
+ "Nitrokey device not found (filter: model={}, serial number in [0xdeadbeef], usb path={})",
+ model.to_string().to_lowercase(),
+ device.path
+ )
+ );
+ } else {
+ assert_eq!(
+ err,
+ format!(
+ "Nitrokey device not found (filter: serial number in [0xdeadbeef], usb path={})",
+ device.path
+ )
+ );
+ }
+ }
+ Ok(())
+}