OTPUri: Fix encoding when generating a URI

Also add some basic tests for now
Fixes #363
This commit is contained in:
Bilal Elmoussaoui 2023-04-07 02:33:01 +02:00
parent d4810bb6f9
commit 4bb81c3cee
2 changed files with 67 additions and 19 deletions

View file

@ -1,6 +1,6 @@
use std::{fmt::Write, str::FromStr};
use percent_encoding::percent_decode_str;
use percent_encoding::{percent_decode_str, utf8_percent_encode, NON_ALPHANUMERIC};
use url::Url;
use crate::{
@ -11,14 +11,14 @@ use crate::{
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone)]
pub struct OTPUri {
pub algorithm: Algorithm,
pub label: String,
pub secret: String,
pub issuer: String,
pub method: Method,
pub digits: Option<u32>,
pub period: Option<u32>,
pub counter: Option<u32>,
pub(crate) algorithm: Algorithm,
pub(crate) label: String,
pub(crate) secret: String,
pub(crate) issuer: String,
pub(crate) method: Method,
pub(crate) digits: Option<u32>,
pub(crate) period: Option<u32>,
pub(crate) counter: Option<u32>,
}
impl RestorableItem for OTPUri {
@ -149,9 +149,9 @@ impl From<OTPUri> for String {
let mut otp_uri = format!(
"otpauth://{}/{}?secret={}&issuer={}&algorithm={}",
val.method.to_string(),
val.label,
utf8_percent_encode(&val.label, NON_ALPHANUMERIC).to_string(),
val.secret,
val.issuer,
utf8_percent_encode(&val.issuer, NON_ALPHANUMERIC).to_string(),
val.algorithm.to_string(),
);
if let Some(digits) = val.digits {
@ -190,3 +190,50 @@ impl From<&Account> for OTPUri {
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::OTPUri;
use crate::{
backup::RestorableItem,
models::{Algorithm, Method},
};
#[test]
fn uri_decode() {
let uri = OTPUri::from_str(
"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example",
)
.unwrap();
assert_eq!(uri.method(), Method::TOTP);
assert_eq!(uri.issuer(), "Example");
assert_eq!(uri.secret(), "JBSWY3DPEHPK3PXP");
assert_eq!(uri.account(), "alice@google.com");
let uri = OTPUri::from_str("otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30").unwrap();
assert_eq!(uri.period(), Some(30));
assert_eq!(uri.digits(), Some(6));
assert_eq!(uri.algorithm(), Algorithm::SHA1);
assert_eq!(uri.issuer(), "ACME Co");
assert_eq!(uri.secret(), "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ");
assert_eq!(uri.account(), "john.doe@email.com");
assert_eq!(uri.method(), Method::TOTP);
}
#[test]
fn uri_encode() {
let uri = OTPUri {
algorithm: Algorithm::SHA1,
label: "account test".to_owned(),
secret: "dznF36H0IIg17rK".to_owned(),
issuer: "Test".to_owned(),
method: Method::TOTP,
digits: Some(6),
period: Some(30),
counter: None,
};
assert_eq!(String::from(uri), "otpauth://totp/account%20test?secret=dznF36H0IIg17rK&issuer=Test&algorithm=sha1&digits=6&period=30");
}
}

View file

@ -5,6 +5,7 @@ use gtk::{glib, prelude::*, subclass::prelude::*};
use once_cell::sync::{Lazy, OnceCell};
use crate::{
backup::RestorableItem,
models::{otp, Account, OTPUri, Provider, ProvidersModel},
widgets::{providers::ProviderPage, Camera, ErrorRevealer, ProviderImage, UrlRow},
};
@ -250,19 +251,19 @@ impl AccountAddDialog {
let imp = self.imp();
imp.deck.set_visible_child_name("main"); // Switch back the form view
imp.token_entry.set_text(&otp_uri.secret);
imp.username_entry.set_text(&otp_uri.label);
imp.token_entry.set_text(&otp_uri.secret());
imp.username_entry.set_text(&otp_uri.account());
let provider = self
.model()
.find_or_create(
&otp_uri.issuer,
otp_uri.period,
otp_uri.method,
&otp_uri.issuer(),
otp_uri.period(),
otp_uri.method(),
None,
otp_uri.algorithm,
otp_uri.digits,
otp_uri.counter,
otp_uri.algorithm(),
otp_uri.digits(),
otp_uri.counter(),
None,
None,
)