Implement OTP clipboard and QR TUI
This commit is contained in:
@@ -6,9 +6,11 @@ use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use ironstorage::{
|
||||
command::{
|
||||
CopyRequest, GenerateRequest, GeneratedPresentation, GrepRequest, InitRequest, InsertInput,
|
||||
InsertRequest, MoveRequest, RemoveRequest,
|
||||
InsertRequest, MoveRequest, OtpAppendRequest, OtpInputSource, OtpInsertRequest,
|
||||
RemoveRequest,
|
||||
},
|
||||
crypto::KeyInfo,
|
||||
otp::OtpInput,
|
||||
write::{InsertContent, OverwriteDecision},
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
@@ -135,6 +137,23 @@ pub struct TransferForm {
|
||||
focus: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum OtpFormKind {
|
||||
Insert,
|
||||
Append,
|
||||
Validate,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OtpForm {
|
||||
kind: OtpFormKind,
|
||||
entry: String,
|
||||
uri: SecretText,
|
||||
force: bool,
|
||||
confirmed: bool,
|
||||
focus: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WorkflowForm {
|
||||
Init(InitForm),
|
||||
@@ -144,6 +163,7 @@ pub enum WorkflowForm {
|
||||
Remove(RemoveForm),
|
||||
Move(TransferForm),
|
||||
Copy(TransferForm),
|
||||
Otp(OtpForm),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -168,6 +188,17 @@ pub enum WorkflowSubmission {
|
||||
request: CopyRequest,
|
||||
overwrite: OverwriteDecision,
|
||||
},
|
||||
OtpInsert {
|
||||
request: OtpInsertRequest,
|
||||
input: OtpInput,
|
||||
},
|
||||
OtpAppend {
|
||||
request: OtpAppendRequest,
|
||||
input: OtpInput,
|
||||
},
|
||||
OtpValidate {
|
||||
uri: OtpInput,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@@ -179,6 +210,16 @@ pub enum WorkflowInput {
|
||||
}
|
||||
|
||||
impl WorkflowForm {
|
||||
pub fn otp(kind: OtpFormKind, entry: Option<String>, force: bool) -> Self {
|
||||
Self::Otp(OtpForm {
|
||||
kind,
|
||||
entry: entry.unwrap_or_default(),
|
||||
uri: SecretText::default(),
|
||||
force,
|
||||
confirmed: false,
|
||||
focus: 0,
|
||||
})
|
||||
}
|
||||
pub fn init(keys: &[KeyInfo], default: Option<&KeyInfo>, request: Option<InitRequest>) -> Self {
|
||||
let request = request.unwrap_or_else(|| InitRequest {
|
||||
path: None,
|
||||
@@ -328,6 +369,11 @@ impl WorkflowForm {
|
||||
Self::Remove(_) => "Remove entry or folder",
|
||||
Self::Move(_) => "Move or rename",
|
||||
Self::Copy(_) => "Copy entry or folder",
|
||||
Self::Otp(form) => match form.kind {
|
||||
OtpFormKind::Insert => "Insert OTP URI",
|
||||
OtpFormKind::Append => "Append OTP URI",
|
||||
OtpFormKind::Validate => "Validate OTP URI",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,6 +470,48 @@ impl WorkflowForm {
|
||||
yes_no(form.overwrite),
|
||||
),
|
||||
],
|
||||
Self::Otp(form) => {
|
||||
vec![
|
||||
row(
|
||||
form.focus == 0,
|
||||
"Entry",
|
||||
if form.kind == OtpFormKind::Validate {
|
||||
"(validation only)"
|
||||
} else if form.entry.is_empty() {
|
||||
"(derive from URI)"
|
||||
} else {
|
||||
&form.entry
|
||||
},
|
||||
),
|
||||
row(
|
||||
form.focus == 1,
|
||||
"OTP URI",
|
||||
if form.uri.is_empty() {
|
||||
"(empty)"
|
||||
} else {
|
||||
"••••••••"
|
||||
},
|
||||
),
|
||||
row(
|
||||
form.focus == 2,
|
||||
"Force replacement",
|
||||
if form.kind == OtpFormKind::Validate {
|
||||
"(not used)"
|
||||
} else {
|
||||
yes_no(form.force)
|
||||
},
|
||||
),
|
||||
row(
|
||||
form.focus == 3,
|
||||
"Confirm write",
|
||||
if form.kind == OtpFormKind::Validate {
|
||||
"(not required)"
|
||||
} else {
|
||||
yes_no(form.confirmed)
|
||||
},
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,6 +661,47 @@ impl WorkflowForm {
|
||||
overwrite,
|
||||
})
|
||||
}
|
||||
Self::Otp(form) => {
|
||||
if form.uri.is_empty() {
|
||||
return Err("OTP URI is required".to_owned());
|
||||
}
|
||||
let input = OtpInput::line(form.uri.bytes()).map_err(|error| error.to_string())?;
|
||||
match form.kind {
|
||||
OtpFormKind::Validate => Ok(WorkflowSubmission::OtpValidate { uri: input }),
|
||||
OtpFormKind::Insert => {
|
||||
if !form.confirmed {
|
||||
return Err("Explicitly confirm the OTP write".to_owned());
|
||||
}
|
||||
Ok(WorkflowSubmission::OtpInsert {
|
||||
request: OtpInsertRequest {
|
||||
entry: (!form.entry.trim().is_empty())
|
||||
.then(|| form.entry.trim().to_owned()),
|
||||
force: form.force,
|
||||
echo: false,
|
||||
source: OtpInputSource::Uri,
|
||||
},
|
||||
input,
|
||||
})
|
||||
}
|
||||
OtpFormKind::Append => {
|
||||
if form.entry.trim().is_empty() {
|
||||
return Err("Entry path is required".to_owned());
|
||||
}
|
||||
if !form.confirmed {
|
||||
return Err("Explicitly confirm the OTP write".to_owned());
|
||||
}
|
||||
Ok(WorkflowSubmission::OtpAppend {
|
||||
request: OtpAppendRequest {
|
||||
entry: form.entry.trim().to_owned(),
|
||||
force: form.force,
|
||||
echo: false,
|
||||
source: OtpInputSource::Uri,
|
||||
},
|
||||
input,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,6 +711,7 @@ impl WorkflowForm {
|
||||
Self::Insert(_) | Self::Generate(_) => 5,
|
||||
Self::Grep(_) => 5,
|
||||
Self::Remove(_) | Self::Move(_) | Self::Copy(_) => 4,
|
||||
Self::Otp(_) => 4,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,6 +724,7 @@ impl WorkflowForm {
|
||||
Self::Grep(form) => &mut form.focus,
|
||||
Self::Remove(form) => &mut form.focus,
|
||||
Self::Move(form) | Self::Copy(form) => &mut form.focus,
|
||||
Self::Otp(form) => &mut form.focus,
|
||||
};
|
||||
*focus = if forward {
|
||||
(*focus + 1) % count
|
||||
@@ -631,6 +762,8 @@ impl WorkflowForm {
|
||||
form.destination_directory ^= true
|
||||
}
|
||||
Self::Move(form) | Self::Copy(form) if form.focus == 3 => form.overwrite ^= true,
|
||||
Self::Otp(form) if form.focus == 2 => form.force ^= true,
|
||||
Self::Otp(form) if form.focus == 3 => form.confirmed ^= true,
|
||||
_ => self.character(' '),
|
||||
}
|
||||
}
|
||||
@@ -674,6 +807,12 @@ impl WorkflowForm {
|
||||
Self::Move(form) | Self::Copy(form) if form.focus == 1 => {
|
||||
form.destination.pop();
|
||||
}
|
||||
Self::Otp(form) if form.focus == 0 => {
|
||||
form.entry.pop();
|
||||
}
|
||||
Self::Otp(form) if form.focus == 1 => {
|
||||
form.uri.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -702,6 +841,8 @@ impl WorkflowForm {
|
||||
Self::Move(form) | Self::Copy(form) if form.focus == 1 => {
|
||||
form.destination.push(character)
|
||||
}
|
||||
Self::Otp(form) if form.focus == 0 => form.entry.push(character),
|
||||
Self::Otp(form) if form.focus == 1 && character != '\n' => form.uri.push(character),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user