Skip to content

Commit cbe2c72

Browse files
authored
fix: Misc OTP Uri import changes (#425)
2 parents 59d3837 + 8814a5e commit cbe2c72

File tree

5 files changed

+69
-7
lines changed

5 files changed

+69
-7
lines changed

src/args.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ enum CotpSubcommands {
3636

3737
#[derive(Args)]
3838
pub struct AddArgs {
39+
/// Add OTP code via an OTP URI
40+
#[arg(short = 'u', long = "otpuri", required_unless_present = "label")]
41+
pub otp_uri: bool,
42+
3943
/// Specify the OTP code type
4044
#[arg(short = 't', long = "type", default_value = "totp")]
4145
pub otp_type: OTPType,
@@ -45,7 +49,7 @@ pub struct AddArgs {
4549
pub issuer: String,
4650

4751
/// Code label
48-
#[arg(short, long, required = true)]
52+
#[arg(short, long, required_unless_present = "otp_uri")]
4953
pub label: Option<String>,
5054

5155
/// OTP Algorithm

src/argument_functions.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::importers::authy_remote_debug::AuthyExportedList;
77
use crate::importers::converted::ConvertedJsonList;
88
use crate::importers::freeotp_plus::FreeOTPPlusJson;
99
use crate::importers::importer::import_from_path;
10+
use crate::otp::from_otp_uri::FromOtpUri;
1011
use crate::otp::otp_element::{OTPDatabase, OTPElement};
1112
use crate::{clipboard, utils};
1213
use color_eyre::eyre::{eyre, ErrReport};
@@ -48,8 +49,14 @@ pub fn import(matches: ImportArgs, mut database: OTPDatabase) -> color_eyre::Res
4849
}
4950

5051
pub fn add(matches: AddArgs, mut database: OTPDatabase) -> color_eyre::Result<OTPDatabase> {
51-
let otp_element = get_from_args(matches)?;
52-
52+
let otp_element = if matches.otp_uri {
53+
let mut otp_uri = rpassword::prompt_password("Insert the otp uri: ").unwrap();
54+
let result = OTPElement::from_otp_uri(otp_uri.as_str());
55+
otp_uri.zeroize();
56+
result?
57+
} else {
58+
get_from_args(matches)?
59+
};
5360
if !otp_element.valid_secret() {
5461
return Err(ErrReport::msg("Invalid secret."));
5562
}

src/importers/importer.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
use std::{error::Error, fmt::Debug, fs::read_to_string, path::PathBuf};
1+
use std::{fmt::Debug, fs::read_to_string, path::PathBuf};
22

3+
use color_eyre::eyre::{eyre, Result};
34
use serde::Deserialize;
45

56
use crate::otp::otp_element::OTPElement;
67

78
/// Common flow for all the importers
8-
pub fn import_from_path<T>(path: PathBuf) -> Result<Vec<OTPElement>, Box<dyn Error>>
9+
pub fn import_from_path<T>(path: PathBuf) -> Result<Vec<OTPElement>>
910
where
1011
T: for<'a> Deserialize<'a> + TryInto<Vec<OTPElement>>,
1112
<T as TryInto<Vec<OTPElement>>>::Error: Debug,
1213
{
1314
let json = read_to_string(path)?;
14-
let deserialized: T = serde_json::from_str(json.as_str()).map_err(|e| e.to_string())?;
15-
let mapped: Vec<OTPElement> = deserialized.try_into().map_err(|e| format!("{:?}", e))?;
15+
let deserialized: T = serde_json::from_str(json.as_str()).map_err(|e| {
16+
eyre!(
17+
"Invalid JSON import format.
18+
Please check the file you are trying to import. For further information please check these guidelines:
19+
https://github.com/replydev/cotp?tab=readme-ov-file#migration-from-other-apps
20+
21+
Specific error: {:?}",
22+
e
23+
)
24+
})?;
25+
let mapped: Vec<OTPElement> = deserialized.try_into().map_err(|e| eyre!("{:?}", e))?;
1626
Ok(mapped)
1727
}

src/importers/otp_uri.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,39 @@ impl TryFrom<OtpUriList> for Vec<OTPElement> {
1414
.collect()
1515
}
1616
}
17+
18+
#[cfg(test)]
19+
mod tests {
20+
use std::path::PathBuf;
21+
22+
use crate::{
23+
exporters::otp_uri::OtpUriList, importers::importer::import_from_path,
24+
otp::otp_element::OTPElement,
25+
};
26+
27+
#[test]
28+
fn test_conversion() {
29+
//Arrange
30+
let expected_element = OTPElement {
31+
secret: String::from("AA"),
32+
issuer: String::default(),
33+
label: String::from("test"),
34+
digits: 6,
35+
type_: crate::otp::otp_type::OTPType::Totp,
36+
algorithm: crate::otp::otp_algorithm::OTPAlgorithm::Sha1,
37+
period: 30,
38+
counter: None,
39+
pin: None,
40+
};
41+
42+
// Act
43+
let mut imported = import_from_path::<OtpUriList>(PathBuf::from(
44+
"test_samples/otp_uri/input_otp_uri.json",
45+
))
46+
.unwrap();
47+
48+
// Assert
49+
assert_eq!(1, imported.len());
50+
assert_eq!(expected_element, imported.pop().unwrap())
51+
}
52+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"items": [
3+
"otpauth://totp/:test?secret=AA&algorithm=SHA1&digits=6&period=30&lock=false"
4+
]
5+
}

0 commit comments

Comments
 (0)