aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2018-12-25 10:41:06 +0100
committerDaniel Mueller <deso@posteo.net>2018-12-27 11:12:59 -0800
commit96adac9c5d3399a4890f54515a92472067771303 (patch)
tree99f36d2a81f46f7bb44ac0374743fd78cdb60092
parentf3c13434e91eea3bcceaa95b86bfcd0db1ec56d4 (diff)
downloadnitrocli-96adac9c5d3399a4890f54515a92472067771303.tar.gz
nitrocli-96adac9c5d3399a4890f54515a92472067771303.tar.bz2
Implement the config set subcommand
This change implements the config set subcommand. The subcommand changes the configuration of a Nitrokey device. Its structure is more complex as it allows partial modifications: The user does not have to change all settings, but may choose to change only some. At the same time, the binding settings can be either set to a value or disabled. Therefore, we have the --{num,caps,scrol}lock options to set a value and the --no-{num,caps,scrol}lock options to disable the value. If none of the two is set, the setting is not changed.
-rw-r--r--nitrocli/src/args.rs107
-rw-r--r--nitrocli/src/commands.rs24
2 files changed, 130 insertions, 1 deletions
diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs
index 298b157..36da560 100644
--- a/nitrocli/src/args.rs
+++ b/nitrocli/src/args.rs
@@ -88,12 +88,14 @@ impl str::FromStr for Command {
#[derive(Debug)]
enum ConfigCommand {
Get,
+ Set,
}
impl ConfigCommand {
fn execute(&self, args: Vec<String>) -> Result<()> {
match *self {
ConfigCommand::Get => config_get(args),
+ ConfigCommand::Set => config_set(args),
}
}
}
@@ -105,6 +107,7 @@ impl fmt::Display for ConfigCommand {
"{}",
match *self {
ConfigCommand::Get => "get",
+ ConfigCommand::Set => "set",
}
)
}
@@ -116,11 +119,47 @@ impl str::FromStr for ConfigCommand {
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
match s {
"get" => Ok(ConfigCommand::Get),
+ "set" => Ok(ConfigCommand::Set),
_ => Err(()),
}
}
}
+#[derive(Clone, Copy, Debug)]
+pub enum ConfigOption<T> {
+ Enable(T),
+ Disable,
+ Ignore,
+}
+
+impl<T> ConfigOption<T> {
+ fn try_from(disable: bool, value: Option<T>, name: &'static str) -> Result<Self> {
+ if disable {
+ if value.is_some() {
+ Err(Error::Error(format!(
+ "--{name} and --no-{name} are mutually exclusive",
+ name = name
+ )))
+ } else {
+ Ok(ConfigOption::Disable)
+ }
+ } else {
+ match value {
+ Some(value) => Ok(ConfigOption::Enable(value)),
+ None => Ok(ConfigOption::Ignore),
+ }
+ }
+ }
+
+ pub fn or(self, default: Option<T>) -> Option<T> {
+ match self {
+ ConfigOption::Enable(value) => Some(value),
+ ConfigOption::Disable => None,
+ ConfigOption::Ignore => default,
+ }
+ }
+}
+
#[derive(Debug)]
enum OtpCommand {
Clear,
@@ -317,6 +356,74 @@ fn config_get(args: Vec<String>) -> Result<()> {
commands::config_get()
}
+/// Write the Nitrokey configuration.
+fn config_set(args: Vec<String>) -> Result<()> {
+ let mut numlock = None;
+ let mut no_numlock = false;
+ let mut capslock = None;
+ let mut no_capslock = false;
+ let mut scrollock = None;
+ let mut no_scrollock = false;
+ let mut otp_pin = false;
+ let mut no_otp_pin = false;
+ let mut parser = argparse::ArgumentParser::new();
+ parser.set_description("Changes the Nitrokey configuration");
+ let _ = parser.refer(&mut numlock).add_option(
+ &["-n", "--numlock"],
+ argparse::StoreOption,
+ "Set the numlock option to the given HOTP slot",
+ );
+ let _ = parser.refer(&mut no_numlock).add_option(
+ &["-N", "--no-numlock"],
+ argparse::StoreTrue,
+ "Unset the numlock option",
+ );
+ let _ = parser.refer(&mut capslock).add_option(
+ &["-c", "--capslock"],
+ argparse::StoreOption,
+ "Set the capslock option to the given HOTP slot",
+ );
+ let _ = parser.refer(&mut no_capslock).add_option(
+ &["-C", "--no-capslock"],
+ argparse::StoreTrue,
+ "Unset the capslock option",
+ );
+ let _ = parser.refer(&mut scrollock).add_option(
+ &["-s", "--scrollock"],
+ argparse::StoreOption,
+ "Set the scrollock option to the given HOTP slot",
+ );
+ let _ = parser.refer(&mut no_scrollock).add_option(
+ &["-S", "--no-scrollock"],
+ argparse::StoreTrue,
+ "Unset the scrollock option",
+ );
+ let _ = parser.refer(&mut otp_pin).add_option(
+ &["-o", "--otp-pin"],
+ argparse::StoreTrue,
+ "Require the user PIN to generate one-time passwords",
+ );
+ let _ = parser.refer(&mut no_otp_pin).add_option(
+ &["-O", "--no-otp-pin"],
+ argparse::StoreTrue,
+ "Allow one-time password generation without PIN",
+ );
+ parse(&parser, args)?;
+ drop(parser);
+
+ let numlock = ConfigOption::try_from(no_numlock, numlock, "numlock")?;
+ let capslock = ConfigOption::try_from(no_capslock, capslock, "capslock")?;
+ let scrollock = ConfigOption::try_from(no_scrollock, scrollock, "scrollock")?;
+ let otp_pin = if otp_pin {
+ Some(true)
+ } else if no_otp_pin {
+ Some(false)
+ } else {
+ None
+ };
+ commands::config_set(numlock, capslock, scrollock, otp_pin)
+}
+
/// Execute an OTP subcommand.
fn otp(args: Vec<String>) -> Result<()> {
let mut subcommand = OtpCommand::Get;
diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs
index 8ce1076..9aef2de 100644
--- a/nitrocli/src/commands.rs
+++ b/nitrocli/src/commands.rs
@@ -128,7 +128,7 @@ where
{
let mut data = data;
let mut retry = 3;
- let mut error_msg: Option<&str> = None;
+ let mut error_msg = None;
loop {
let passphrase = match pinentry::inquire_passphrase(pin, error_msg) {
Ok(passphrase) => passphrase,
@@ -304,6 +304,28 @@ pub fn config_get() -> Result<()> {
Ok(())
}
+/// Write the Nitrokey configuration.
+pub fn config_set(
+ numlock: args::ConfigOption<u8>,
+ capslock: args::ConfigOption<u8>,
+ scrollock: args::ConfigOption<u8>,
+ user_password: Option<bool>,
+) -> Result<()> {
+ let device = authenticate_admin(get_device()?)?;
+ let config = device
+ .get_config()
+ .map_err(|err| get_error("Could not get configuration", &err))?;
+ let config = nitrokey::Config {
+ numlock: numlock.or(config.numlock),
+ capslock: capslock.or(config.capslock),
+ scrollock: scrollock.or(config.scrollock),
+ user_password: user_password.unwrap_or(config.user_password),
+ };
+ device
+ .write_config(config)
+ .map_err(|err| get_error("Could not set configuration", &err))
+}
+
fn get_otp<T: GenerateOtp>(slot: u8, algorithm: args::OtpAlgorithm, device: &T) -> Result<String> {
match algorithm {
args::OtpAlgorithm::Hotp => device.get_hotp_code(slot),