From 0cb2ac01371523eae785c12ce88848039090c1e6 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 23 Aug 2020 16:17:42 +0200 Subject: Merge ExecCtx and RunCtx into Context Since we moved the model, no_cache and verbosity fields from ExecCtx into Config and added a Config field to both ExecCtx and RunCtx, RunCtx and ExecCtx are identical. Therefore this patch merges the ExecCtx and RunCtx structs into the new Context struct. --- src/arg_util.rs | 2 +- src/commands.rs | 94 ++++++++++++++++++++++++++++---------------------------- src/main.rs | 46 +++++---------------------- src/pinentry.rs | 6 ++-- src/tests/mod.rs | 4 +-- 5 files changed, 60 insertions(+), 92 deletions(-) diff --git a/src/arg_util.rs b/src/arg_util.rs index be361c7..d4ffa74 100644 --- a/src/arg_util.rs +++ b/src/arg_util.rs @@ -49,7 +49,7 @@ macro_rules! Command { impl $name { pub fn execute( self, - ctx: &mut crate::ExecCtx<'_>, + ctx: &mut crate::Context<'_>, ) -> anyhow::Result<()> { match self { $( diff --git a/src/commands.rs b/src/commands.rs index 090d532..4702001 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -35,10 +35,10 @@ use nitrokey::GetPasswordSafe; use crate::args; use crate::pinentry; -use crate::ExecCtx; +use crate::Context; /// Set `libnitrokey`'s log level based on the execution context's verbosity. -fn set_log_level(ctx: &mut ExecCtx<'_>) { +fn set_log_level(ctx: &mut Context<'_>) { let log_lvl = match ctx.config.verbosity { // The error log level is what libnitrokey uses by default. As such, // there is no harm in us setting that as well when the user did not @@ -54,9 +54,9 @@ fn set_log_level(ctx: &mut ExecCtx<'_>) { } /// Connect to any Nitrokey device and do something with it. -fn with_device(ctx: &mut ExecCtx<'_>, op: F) -> anyhow::Result<()> +fn with_device(ctx: &mut Context<'_>, op: F) -> anyhow::Result<()> where - F: FnOnce(&mut ExecCtx<'_>, nitrokey::DeviceWrapper<'_>) -> anyhow::Result<()>, + F: FnOnce(&mut Context<'_>, nitrokey::DeviceWrapper<'_>) -> anyhow::Result<()>, { let mut manager = nitrokey::take().context("Failed to acquire access to Nitrokey device manager")?; @@ -74,9 +74,9 @@ where } /// Connect to a Nitrokey Storage device and do something with it. -fn with_storage_device(ctx: &mut ExecCtx<'_>, op: F) -> anyhow::Result<()> +fn with_storage_device(ctx: &mut Context<'_>, op: F) -> anyhow::Result<()> where - F: FnOnce(&mut ExecCtx<'_>, nitrokey::Storage<'_>) -> anyhow::Result<()>, + F: FnOnce(&mut Context<'_>, nitrokey::Storage<'_>) -> anyhow::Result<()>, { let mut manager = nitrokey::take().context("Failed to acquire access to Nitrokey device manager")?; @@ -97,9 +97,9 @@ where /// Connect to any Nitrokey device, retrieve a password safe handle, and /// do something with it. -fn with_password_safe(ctx: &mut ExecCtx<'_>, mut op: F) -> anyhow::Result<()> +fn with_password_safe(ctx: &mut Context<'_>, mut op: F) -> anyhow::Result<()> where - F: FnMut(&mut ExecCtx<'_>, nitrokey::PasswordSafe<'_, '_>) -> anyhow::Result<()>, + F: FnMut(&mut Context<'_>, nitrokey::PasswordSafe<'_, '_>) -> anyhow::Result<()>, { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?; @@ -118,14 +118,14 @@ where /// Authenticate the given device using the given PIN type and operation. fn authenticate<'mgr, D, A, F>( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, device: D, pin_type: args::PinType, op: F, ) -> anyhow::Result where D: Device<'mgr>, - F: FnMut(&mut ExecCtx<'_>, D, &str) -> Result, + F: FnMut(&mut Context<'_>, D, &str) -> Result, { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; @@ -134,7 +134,7 @@ where /// Authenticate the given device with the user PIN. fn authenticate_user<'mgr, T>( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, device: T, ) -> anyhow::Result> where @@ -151,7 +151,7 @@ where /// Authenticate the given device with the admin PIN. fn authenticate_admin<'mgr, T>( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, device: T, ) -> anyhow::Result> where @@ -194,13 +194,13 @@ fn get_volume_status(status: &nitrokey::VolumeStatus) -> &'static str { /// second or third try, it will call `op` with the data returned by the /// previous call to `op`. fn try_with_pin_and_data_with_pinentry( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, pin_entry: &pinentry::PinEntry, data: D, mut op: F, ) -> anyhow::Result where - F: FnMut(&mut ExecCtx<'_>, D, &str) -> Result, + F: FnMut(&mut Context<'_>, D, &str) -> Result, { let mut data = data; let mut retry = 3; @@ -232,17 +232,17 @@ where /// Try to execute the given function with a PIN. fn try_with_pin_and_data( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, pin_entry: &pinentry::PinEntry, data: D, mut op: F, ) -> anyhow::Result where - F: FnMut(&mut ExecCtx<'_>, D, &str) -> Result, + F: FnMut(&mut Context<'_>, D, &str) -> Result, { let pin = match pin_entry.pin_type() { // Ideally we would not clone here, but that would require us to - // restrict op to work with an immutable ExecCtx, which is not + // restrict op to work with an immutable Context, which is not // possible given that some clients print data. args::PinType::Admin => ctx.admin_pin.clone(), args::PinType::User => ctx.user_pin.clone(), @@ -263,7 +263,7 @@ where /// This function behaves exactly as `try_with_pin_and_data`, but /// it refrains from passing any data to it. fn try_with_pin( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, pin_entry: &pinentry::PinEntry, mut op: F, ) -> anyhow::Result<()> @@ -277,7 +277,7 @@ where /// Pretty print the status of a Nitrokey Storage. fn print_storage_status( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, status: &nitrokey::StorageStatus, ) -> anyhow::Result<()> { println!( @@ -310,7 +310,7 @@ fn print_storage_status( /// Query and pretty print the status that is common to all Nitrokey devices. fn print_status( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, model: &'static str, device: &nitrokey::DeviceWrapper<'_>, ) -> anyhow::Result<()> { @@ -351,7 +351,7 @@ fn print_status( } /// Inquire the status of the nitrokey. -pub fn status(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn status(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |ctx, device| { let model = match device { nitrokey::DeviceWrapper::Pro(_) => "Pro", @@ -362,7 +362,7 @@ pub fn status(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } /// List the attached Nitrokey devices. -pub fn list(ctx: &mut ExecCtx<'_>, no_connect: bool) -> anyhow::Result<()> { +pub fn list(ctx: &mut Context<'_>, no_connect: bool) -> anyhow::Result<()> { set_log_level(ctx); let device_infos = @@ -407,7 +407,7 @@ pub fn list(ctx: &mut ExecCtx<'_>, no_connect: bool) -> anyhow::Result<()> { } /// Perform a factory reset. -pub fn reset(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn reset(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(args::PinType::Admin, &device)?; @@ -436,7 +436,7 @@ pub fn reset(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { /// Change the configuration of the unencrypted volume. pub fn unencrypted_set( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, mode: args::UnencryptedVolumeMode, ) -> anyhow::Result<()> { with_storage_device(ctx, |ctx, mut device| { @@ -459,7 +459,7 @@ pub fn unencrypted_set( } /// Open the encrypted volume on the Nitrokey. -pub fn encrypted_open(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn encrypted_open(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_storage_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?; @@ -476,7 +476,7 @@ pub fn encrypted_open(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } /// Close the previously opened encrypted volume. -pub fn encrypted_close(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn encrypted_close(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_storage_device(ctx, |_ctx, mut device| { // Flush all filesystem caches to disk. We are mostly interested in // making sure that the encrypted volume on the Nitrokey we are @@ -491,7 +491,7 @@ pub fn encrypted_close(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } /// Create a hidden volume. -pub fn hidden_create(ctx: &mut ExecCtx<'_>, slot: u8, start: u8, end: u8) -> anyhow::Result<()> { +pub fn hidden_create(ctx: &mut Context<'_>, slot: u8, start: u8, end: u8) -> anyhow::Result<()> { with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { @@ -510,7 +510,7 @@ pub fn hidden_create(ctx: &mut ExecCtx<'_>, slot: u8, start: u8, end: u8) -> any } /// Open a hidden volume. -pub fn hidden_open(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn hidden_open(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { @@ -534,7 +534,7 @@ pub fn hidden_open(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } /// Close a previously opened hidden volume. -pub fn hidden_close(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn hidden_close(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_storage_device(ctx, |_ctx, mut device| { unsafe { sync() }; @@ -553,7 +553,7 @@ fn format_option(option: Option) -> String { } /// Read the Nitrokey configuration. -pub fn config_get(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn config_get(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |ctx, device| { let config = device.get_config().context("Failed to get configuration")?; println!( @@ -573,7 +573,7 @@ pub fn config_get(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } /// Write the Nitrokey configuration. -pub fn config_set(ctx: &mut ExecCtx<'_>, args: args::ConfigSetArgs) -> anyhow::Result<()> { +pub fn config_set(ctx: &mut Context<'_>, args: args::ConfigSetArgs) -> anyhow::Result<()> { let numlock = args::ConfigOption::try_from(args.no_numlock, args.numlock, "numlock") .context("Failed to apply numlock configuration")?; let capslock = args::ConfigOption::try_from(args.no_capslock, args.capslock, "capslock") @@ -606,7 +606,7 @@ pub fn config_set(ctx: &mut ExecCtx<'_>, args: args::ConfigSetArgs) -> anyhow::R } /// Lock the Nitrokey device. -pub fn lock(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn lock(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |_ctx, mut device| { device.lock().context("Failed to lock the device") }) @@ -632,7 +632,7 @@ fn get_unix_timestamp() -> anyhow::Result { /// Generate a one-time password on the Nitrokey device. pub fn otp_get( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, slot: u8, algorithm: args::OtpAlgorithm, time: Option, @@ -693,7 +693,7 @@ fn prepare_base32_secret(secret: &str) -> anyhow::Result { } /// Configure a one-time password slot on the Nitrokey device. -pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: args::OtpSetArgs) -> anyhow::Result<()> { +pub fn otp_set(ctx: &mut Context<'_>, mut args: args::OtpSetArgs) -> anyhow::Result<()> { let mut data = nitrokey::OtpSlotData { number: args.slot, name: mem::take(&mut args.name), @@ -733,7 +733,7 @@ pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: args::OtpSetArgs) -> anyhow::Res /// Clear an OTP slot. pub fn otp_clear( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, slot: u8, algorithm: args::OtpAlgorithm, ) -> anyhow::Result<()> { @@ -749,7 +749,7 @@ pub fn otp_clear( } fn print_otp_status( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, algorithm: args::OtpAlgorithm, device: &nitrokey::DeviceWrapper<'_>, all: bool, @@ -780,7 +780,7 @@ fn print_otp_status( } /// Print the status of the OTP slots. -pub fn otp_status(ctx: &mut ExecCtx<'_>, all: bool) -> anyhow::Result<()> { +pub fn otp_status(ctx: &mut Context<'_>, all: bool) -> anyhow::Result<()> { with_device(ctx, |ctx, device| { println!(ctx, "alg\tslot\tname")?; print_otp_status(ctx, args::OtpAlgorithm::Hotp, &device, all)?; @@ -790,7 +790,7 @@ pub fn otp_status(ctx: &mut ExecCtx<'_>, all: bool) -> anyhow::Result<()> { } /// Clear the PIN stored by various operations. -pub fn pin_clear(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn pin_clear(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |_ctx, device| { pinentry::clear(&pinentry::PinEntry::from(args::PinType::Admin, &device)?) .context("Failed to clear admin PIN")?; @@ -805,7 +805,7 @@ pub fn pin_clear(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { /// If the user has set the respective environment variable for the /// given PIN type, it will be used. fn choose_pin( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, pin_entry: &pinentry::PinEntry, new: bool, ) -> anyhow::Result { @@ -837,7 +837,7 @@ fn choose_pin( } /// Change a PIN. -pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: args::PinType) -> anyhow::Result<()> { +pub fn pin_set(ctx: &mut Context<'_>, pin_type: args::PinType) -> anyhow::Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; let new_pin = choose_pin(ctx, &pin_entry, true)?; @@ -859,7 +859,7 @@ pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: args::PinType) -> anyhow::Result } /// Unblock and reset the user PIN. -pub fn pin_unblock(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { +pub fn pin_unblock(ctx: &mut Context<'_>) -> anyhow::Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(args::PinType::User, &device)?; let user_pin = choose_pin(ctx, &pin_entry, false)?; @@ -874,7 +874,7 @@ pub fn pin_unblock(ctx: &mut ExecCtx<'_>) -> anyhow::Result<()> { } fn print_pws_data( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, description: &'static str, result: Result, quiet: bool, @@ -904,7 +904,7 @@ fn check_slot(pws: &nitrokey::PasswordSafe<'_, '_>, slot: u8) -> anyhow::Result< /// Read a PWS slot. pub fn pws_get( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, slot: u8, show_name: bool, show_login: bool, @@ -930,7 +930,7 @@ pub fn pws_get( /// Write a PWS slot. pub fn pws_set( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, slot: u8, name: &str, login: &str, @@ -944,14 +944,14 @@ pub fn pws_set( } /// Clear a PWS slot. -pub fn pws_clear(ctx: &mut ExecCtx<'_>, slot: u8) -> anyhow::Result<()> { +pub fn pws_clear(ctx: &mut Context<'_>, slot: u8) -> anyhow::Result<()> { with_password_safe(ctx, |_ctx, mut pws| { pws.erase_slot(slot).context("Failed to clear PWS slot") }) } fn print_pws_slot( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, pws: &nitrokey::PasswordSafe<'_, '_>, slot: usize, programmed: bool, @@ -969,7 +969,7 @@ fn print_pws_slot( } /// Print the status of all PWS slots. -pub fn pws_status(ctx: &mut ExecCtx<'_>, all: bool) -> anyhow::Result<()> { +pub fn pws_status(ctx: &mut Context<'_>, all: bool) -> anyhow::Result<()> { with_password_safe(ctx, |ctx, pws| { let slots = pws .get_slot_status() diff --git a/src/main.rs b/src/main.rs index 9e52613..79a9c0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,47 +85,14 @@ const NITROCLI_NEW_ADMIN_PIN: &str = "NITROCLI_NEW_ADMIN_PIN"; const NITROCLI_NEW_USER_PIN: &str = "NITROCLI_NEW_USER_PIN"; const NITROCLI_PASSWORD: &str = "NITROCLI_PASSWORD"; -/// A command execution context that captures additional data pertaining -/// the command execution. -#[allow(missing_debug_implementations)] -pub struct ExecCtx<'io> { - /// See `RunCtx::stdout`. - pub stdout: &'io mut dyn io::Write, - /// See `RunCtx::stderr`. - pub stderr: &'io mut dyn io::Write, - /// See `RunCtx::admin_pin`. - pub admin_pin: Option, - /// See `RunCtx::user_pin`. - pub user_pin: Option, - /// See `RunCtx::new_admin_pin`. - pub new_admin_pin: Option, - /// See `RunCtx::new_user_pin`. - pub new_user_pin: Option, - /// See `RunCtx::password`. - pub password: Option, - /// See `RunCtx::config`. - pub config: config::Config, -} - /// Parse the command-line arguments and execute the selected command. -fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec) -> anyhow::Result<()> { +fn handle_arguments(ctx: &mut Context<'_>, args: Vec) -> anyhow::Result<()> { use structopt::StructOpt; match args::Args::from_iter_safe(args.iter()) { Ok(args) => { - let mut config = ctx.config; - config.update(&args); - let mut ctx = ExecCtx { - stdout: ctx.stdout, - stderr: ctx.stderr, - admin_pin: ctx.admin_pin.take(), - user_pin: ctx.user_pin.take(), - new_admin_pin: ctx.new_admin_pin.take(), - new_user_pin: ctx.new_user_pin.take(), - password: ctx.password.take(), - config, - }; - args.cmd.execute(&mut ctx) + ctx.config.update(&args); + args.cmd.execute(ctx) } Err(err) => { if err.use_stderr() { @@ -139,7 +106,8 @@ fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec) -> anyhow::Result<( } /// The context used when running the program. -pub(crate) struct RunCtx<'io> { +#[allow(missing_debug_implementations)] +pub struct Context<'io> { /// The `Write` object used as standard output throughout the program. pub stdout: &'io mut dyn io::Write, /// The `Write` object used as standard error throughout the program. @@ -163,7 +131,7 @@ pub(crate) struct RunCtx<'io> { pub config: config::Config, } -fn run<'ctx, 'io: 'ctx>(ctx: &'ctx mut RunCtx<'io>, args: Vec) -> i32 { +fn run<'ctx, 'io: 'ctx>(ctx: &'ctx mut Context<'io>, args: Vec) -> i32 { match handle_arguments(ctx, args) { Ok(()) => 0, Err(err) => { @@ -182,7 +150,7 @@ fn main() { let rc = match config::Config::load() { Ok(config) => { let args = env::args().collect::>(); - let ctx = &mut RunCtx { + let ctx = &mut Context { stdout: &mut stdout, stderr: &mut stderr, admin_pin: env::var_os(NITROCLI_ADMIN_PIN), diff --git a/src/pinentry.rs b/src/pinentry.rs index f538a47..937d8df 100644 --- a/src/pinentry.rs +++ b/src/pinentry.rs @@ -25,7 +25,7 @@ use std::str; use anyhow::Context as _; use crate::args; -use crate::ExecCtx; +use crate::Context; type CowStr = borrow::Cow<'static, str>; @@ -228,7 +228,7 @@ where /// dialog. It is used to choose an appropriate description and to /// decide whether a quality bar is shown in the dialog. pub fn inquire( - ctx: &mut ExecCtx<'_>, + ctx: &mut Context<'_>, entry: &E, mode: Mode, error_msg: Option<&str>, @@ -283,7 +283,7 @@ where } } -pub fn choose(ctx: &mut ExecCtx<'_>, entry: &E) -> anyhow::Result +pub fn choose(ctx: &mut Context<'_>, entry: &E) -> anyhow::Result where E: SecretEntry, { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 9477964..3c38b8e 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -94,7 +94,7 @@ impl Nitrocli { fn do_run(&mut self, args: &[&str], f: F) -> (R, Vec, Vec) where - F: FnOnce(&mut crate::RunCtx<'_>, Vec) -> R, + F: FnOnce(&mut crate::Context<'_>, Vec) -> R, { let args = ["nitrocli"] .iter() @@ -107,7 +107,7 @@ impl Nitrocli { let mut stdout = Vec::new(); let mut stderr = Vec::new(); - let ctx = &mut crate::RunCtx { + let ctx = &mut crate::Context { stdout: &mut stdout, stderr: &mut stderr, admin_pin: self.admin_pin.clone(), -- cgit v1.2.1