ui: integrate hotp support

This commit is contained in:
Bilal Elmoussaoui 2020-12-06 18:40:06 +01:00
parent aa5cfe50f8
commit 2a0b7a4e5d
14 changed files with 1791 additions and 1354 deletions

View file

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkAdjustment" id="counter_adjustment">
<property name="upper">60</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<template parent="HdyWindow" class="AccountAddDialog">
<property name="modal">True</property>
<property name="default_width">360</property>
@ -45,6 +50,9 @@
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<child>
<object class="HdyClamp">
<property name="valign">center</property>
<property name="margin-top">12</property>
@ -133,6 +141,23 @@
</child>
</object>
</child>
<child>
<object class="HdyActionRow" id="counter_row">
<property name="activatable_widget">counter_spinbutton</property>
<property name="title" translatable="yes">Counter</property>
<property name="visible">False</property>
<child>
<object class="GtkSpinButton" id="counter_spinbutton">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="text" translatable="yes">0</property>
<property name="adjustment">counter_adjustment</property>
<property name="numeric">True</property>
</object>
</child>
</object>
</child>
<style>
<class name="content"/>
</style>
@ -169,7 +194,31 @@
</object>
</child>
<child>
<object class="HdyActionRow">
<object class="HdyActionRow" id="digits_row">
<property name="title" translatable="yes">Digits</property>
<child>
<object class="GtkLabel" id="digits_label">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow" id="hmac_algorithm_row">
<property name="title" translatable="yes">HMAC Algorithm</property>
<child>
<object class="GtkLabel" id="hmac_algorithm_label">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow" id="period_row">
<property name="title" translatable="yes">Period</property>
<child>
<object class="GtkLabel" id="period_label">
@ -202,6 +251,8 @@
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
@ -224,6 +275,7 @@
<widget name="provider_entry"/>
<widget name="username_entry"/>
<widget name="token_entry"/>
<widget name="counter_spinbutton"/>
</widgets>
</object>
</interface>

View file

@ -5,9 +5,21 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="default_counter_adjustment">
<property name="upper">60</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="digits_adjustment">
<property name="upper">60</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkSizeGroup">
<widgets>
<widget name="digits_spinbutton"/>
<widget name="period_spinbutton"/>
<widget name="default_counter_spinbutton"/>
<widget name="provider_website_entry"/>
<widget name="provider_help_entry"/>
<widget name="name_entry"/>
@ -134,7 +146,15 @@
</object>
</child>
<child>
<object class="HdyActionRow">
<object class="HdyComboRow" id="algorithm_comborow">
<property name="title" translatable="yes">Algorithm</property>
<property name="expression">
<lookup type="HdyEnumValueObject" name="name"/>
</property>
</object>
</child>
<child>
<object class="HdyActionRow" id="period_row">
<property name="activatable_widget">period_spinbutton</property>
<property name="title" translatable="yes">Period</property>
<child>
@ -150,13 +170,46 @@
</object>
</child>
<child>
<object class="HdyComboRow" id="algorithm_comborow">
<property name="title" translatable="yes">Algorithm</property>
<object class="HdyComboRow" id="hmac_algorithm_comborow">
<property name="title" translatable="yes">HMAC Algorithm</property>
<property name="expression">
<lookup type="HdyEnumValueObject" name="name"/>
</property>
</object>
</child>
<child>
<object class="HdyActionRow" id="default_counter_row">
<property name="activatable_widget">default_counter_spinbutton</property>
<property name="title" translatable="yes">Default Counter</property>
<child>
<object class="GtkSpinButton" id="default_counter_spinbutton">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="text" translatable="yes">0</property>
<property name="adjustment">default_counter_adjustment</property>
<property name="numeric">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="HdyActionRow" id="digits_row">
<property name="activatable_widget">digits_spinbutton</property>
<property name="title" translatable="yes">Digits</property>
<property name="subtitle" translatable="yes">Defaults to 6</property>
<child>
<object class="GtkSpinButton" id="digits_spinbutton">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="text" translatable="yes">0</property>
<property name="adjustment">digits_adjustment</property>
<property name="numeric">True</property>
</object>
</child>
</object>
</child>
<style>
<class name="content"/>
</style>

View file

@ -2,6 +2,7 @@
CREATE TABLE "accounts" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
"name" VARCHAR NOT NULL,
"counter" INTEGER NULL DEFAULT 1,
"token_id" VARCHAR NOT NULL,
"provider_id" INTEGER NOT NULL
)

View file

@ -5,6 +5,9 @@ CREATE TABLE "providers" (
"website" VARCHAR NULL,
"help_url" VARCHAR NULL,
"image_uri" VARCHAR NULL,
"digits" INTEGER NULL DEFAULT 6,
"period" INTEGER NULL DEFAULT 30,
"default_counter" INTEGER NULL DEFAULT 1,
"hmac_algorithm" VARCHAR NULL DEFAULT "SHA1",
"algorithm" VARCHAR DEFAULT "TOTP"
);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@ struct NewAccount {
pub name: String,
pub token_id: String,
pub provider_id: i32,
pub counter: i32,
}
#[derive(Identifiable, Queryable, Associations, Hash, PartialEq, Eq, Debug, Clone)]
@ -26,6 +27,7 @@ struct NewAccount {
pub struct DiAccount {
pub id: i32,
pub name: String,
pub counter: i32,
pub token_id: String,
pub provider_id: i32,
}
@ -34,11 +36,12 @@ pub struct AccountPriv {
pub id: Cell<i32>,
pub otp: RefCell<String>,
pub name: RefCell<String>,
pub counter: Cell<i32>,
pub token_id: RefCell<String>,
pub provider: RefCell<Option<Provider>>,
}
static PROPERTIES: [subclass::Property; 5] = [
static PROPERTIES: [subclass::Property; 6] = [
subclass::Property("id", |name| {
glib::ParamSpec::int(
name,
@ -50,6 +53,17 @@ static PROPERTIES: [subclass::Property; 5] = [
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("counter", |name| {
glib::ParamSpec::int(
name,
"counter",
"Counter",
0,
i32::MAX,
0,
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("name", |name| {
glib::ParamSpec::string(name, "name", "Name", None, glib::ParamFlags::READWRITE)
}),
@ -98,6 +112,7 @@ impl ObjectSubclass for AccountPriv {
fn new() -> Self {
Self {
id: Cell::new(0),
counter: Cell::new(1),
name: RefCell::new("".to_string()),
otp: RefCell::new("".to_string()),
token_id: RefCell::new("".to_string()),
@ -125,6 +140,13 @@ impl ObjectImpl for AccountPriv {
.unwrap();
self.name.replace(name);
}
subclass::Property("counter", ..) => {
let counter = value
.get()
.expect("type conformity checked by `Object::set_property`")
.unwrap();
self.counter.replace(counter);
}
subclass::Property("otp", ..) => {
let otp = value
.get()
@ -155,6 +177,7 @@ impl ObjectImpl for AccountPriv {
match *prop {
subclass::Property("id", ..) => self.id.get().to_value(),
subclass::Property("name", ..) => self.name.borrow().to_value(),
subclass::Property("counter", ..) => self.counter.get().to_value(),
subclass::Property("otp", ..) => self.otp.borrow().to_value(),
subclass::Property("token-id", ..) => self.token_id.borrow().to_value(),
subclass::Property("provider", ..) => self.provider.borrow().to_value(),
@ -177,6 +200,7 @@ impl Account {
name: name.to_string(),
token_id: token_id.to_string(),
provider_id: provider.id(),
counter: provider.default_counter(),
})
.execute(&conn)?;
@ -189,6 +213,7 @@ impl Account {
account.id,
&account.name,
&account.token_id,
account.counter,
provider.clone(),
)
})
@ -202,7 +227,15 @@ impl Account {
let results = DiAccount::belonging_to(&dip)
.load::<DiAccount>(&conn)?
.into_iter()
.map(|account| Self::new(account.id, &account.name, &account.token_id, p.clone()))
.map(|account| {
Self::new(
account.id,
&account.name,
&account.token_id,
account.counter,
p.clone(),
)
})
.collect::<Vec<Account>>();
Ok(results)
}
@ -214,7 +247,7 @@ impl Account {
account1.name().cmp(&account2.name())
}
pub fn new(id: i32, name: &str, token_id: &str, provider: Provider) -> Account {
pub fn new(id: i32, name: &str, token_id: &str, counter: i32, provider: Provider) -> Account {
let account = glib::Object::new(
Account::static_type(),
&[
@ -222,6 +255,7 @@ impl Account {
("name", &name),
("token-id", &token_id),
("provider", &provider),
("counter", &counter),
],
)
.expect("Failed to create account")

View file

@ -1,7 +1,6 @@
use gettextrs::gettext;
use std::str::FromStr;
use std::string::ToString;
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
#[repr(u32)]
#[genum(type_name = "ProviderAlgorithm")]
@ -13,11 +12,27 @@ pub enum Algorithm {
Steam = 2,
}
impl Default for Algorithm {
fn default() -> Self {
Self::TOTP
}
}
impl From<u32> for Algorithm {
fn from(u: u32) -> Self {
match u {
1 => Algorithm::HOTP,
2 => Algorithm::Steam,
_ => Algorithm::default(),
}
}
}
impl Algorithm {
pub fn to_locale_string(&self) -> String {
match *self {
Algorithm::HOTP => gettext("HMAC-based One-time Password"),
Algorithm::TOTP => gettext("Time-based One-Time-Password"),
Algorithm::HOTP => gettext("HMAC-based"),
Algorithm::TOTP => gettext("Time-based"),
Algorithm::Steam => gettext("Steam"),
}
}
@ -45,3 +60,54 @@ impl ToString for Algorithm {
.to_string()
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, GEnum)]
#[repr(u32)]
#[genum(type_name = "ProviderHOTPAlgorithm")]
pub enum HOTPAlgorithm {
#[genum(name = "SHA1")]
SHA1 = 0,
#[genum(name = "SHA256")]
SHA256 = 1,
#[genum(name = "SHA512")]
SHA512 = 2,
}
impl Default for HOTPAlgorithm {
fn default() -> Self {
Self::SHA1
}
}
impl HOTPAlgorithm {
pub fn to_locale_string(&self) -> String {
match *self {
HOTPAlgorithm::SHA1 => gettext("SHA1"),
HOTPAlgorithm::SHA256 => gettext("SHA256"),
HOTPAlgorithm::SHA512 => gettext("SHA512"),
}
}
}
impl FromStr for HOTPAlgorithm {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"sha1" | "otp" => Ok(Self::SHA1),
"sha256" => Ok(Self::SHA256),
"sha512" => Ok(Self::SHA512),
_ => anyhow::bail!("Unsupported HMAC algorithm"),
}
}
}
impl ToString for HOTPAlgorithm {
fn to_string(&self) -> String {
match *self {
HOTPAlgorithm::SHA1 => "sha1",
HOTPAlgorithm::SHA256 => "sha256",
HOTPAlgorithm::SHA512 => "sha512",
}
.to_string()
}
}

View file

@ -11,7 +11,7 @@ mod providers;
pub use self::account::Account;
pub use self::account_sorter::AccountSorter;
pub use self::accounts::AccountsModel;
pub use self::algorithm::Algorithm;
pub use self::algorithm::{Algorithm, HOTPAlgorithm};
pub use self::favicon::{FaviconError, FaviconScrapper};
pub use self::provider::Provider;
pub use self::provider_sorter::ProviderSorter;

View file

@ -1,4 +1,4 @@
use super::algorithm::Algorithm;
use super::algorithm::{Algorithm, HOTPAlgorithm};
use crate::models::{database, Account, AccountsModel, FaviconError, FaviconScrapper};
use crate::schema::providers;
use anyhow::Result;
@ -19,11 +19,14 @@ use url::Url;
#[table_name = "providers"]
struct NewProvider {
pub name: String,
pub period: i32,
pub algorithm: String,
pub website: Option<String>,
pub help_url: Option<String>,
pub image_uri: Option<String>,
pub period: i32,
pub digits: i32,
pub default_counter: i32,
pub hmac_algorithm: String,
pub algorithm: String,
}
#[derive(Identifiable, Queryable, Associations, Hash, PartialEq, Eq, Debug, Clone)]
@ -31,11 +34,14 @@ struct NewProvider {
pub struct DiProvider {
pub id: i32,
pub name: String,
pub period: i32,
pub algorithm: String,
pub website: Option<String>,
pub help_url: Option<String>,
pub image_uri: Option<String>,
pub period: i32,
pub digits: i32,
pub default_counter: i32,
pub hmac_algorithm: String,
pub algorithm: String,
}
pub struct ProviderPriv {
@ -43,6 +49,9 @@ pub struct ProviderPriv {
pub name: RefCell<String>,
pub period: Cell<i32>,
pub algorithm: RefCell<String>,
pub default_counter: Cell<i32>,
pub hmac_algorithm: RefCell<String>,
pub digits: Cell<i32>,
pub website: RefCell<Option<String>>,
pub help_url: RefCell<Option<String>>,
pub image_uri: RefCell<Option<String>>,
@ -50,7 +59,7 @@ pub struct ProviderPriv {
pub filter_model: gtk::FilterListModel,
}
static PROPERTIES: [subclass::Property; 8] = [
static PROPERTIES: [subclass::Property; 11] = [
subclass::Property("id", |name| {
glib::ParamSpec::int(
name,
@ -85,12 +94,43 @@ static PROPERTIES: [subclass::Property; 8] = [
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("digits", |name| {
glib::ParamSpec::int(
name,
"digits",
"Digits",
0,
1000,
6,
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("default-counter", |name| {
glib::ParamSpec::int(
name,
"default_counter",
"default_counter",
0,
1000,
1,
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("hmac-algorithm", |name| {
glib::ParamSpec::string(
name,
"hmac_algorithm",
"HMAC algorithm",
Some(&HOTPAlgorithm::default().to_string()),
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("algorithm", |name| {
glib::ParamSpec::string(
name,
"algorithm",
"Algorithm",
Some(&Algorithm::TOTP.to_string()),
Some(&Algorithm::default().to_string()),
glib::ParamFlags::READWRITE,
)
}),
@ -140,11 +180,14 @@ impl ObjectSubclass for ProviderPriv {
let model = AccountsModel::new();
Self {
id: Cell::new(0),
default_counter: Cell::new(1),
hmac_algorithm: RefCell::new(HOTPAlgorithm::default().to_string()),
digits: Cell::new(6),
name: RefCell::new("".to_string()),
website: RefCell::new(None),
help_url: RefCell::new(None),
image_uri: RefCell::new(None),
algorithm: RefCell::new(Algorithm::TOTP.to_string()),
algorithm: RefCell::new(Algorithm::default().to_string()),
period: Cell::new(30),
filter_model: gtk::FilterListModel::new(Some(&model), gtk::NONE_FILTER),
accounts: model,
@ -184,6 +227,25 @@ impl ObjectImpl for ProviderPriv {
.unwrap();
self.algorithm.replace(algorithm);
}
subclass::Property("digits", ..) => {
let digits = value
.get_some()
.expect("type conformity checked by `Object::set_property`");
self.digits.replace(digits);
}
subclass::Property("hmac-algorithm", ..) => {
let hmac_algorithm = value
.get()
.expect("type conformity checked by `Object::set_property`")
.unwrap();
self.hmac_algorithm.replace(hmac_algorithm);
}
subclass::Property("default-counter", ..) => {
let default_counter = value
.get_some()
.expect("type conformity checked by `Object::set_property`");
self.default_counter.replace(default_counter);
}
subclass::Property("website", ..) => {
let website = value
.get()
@ -214,6 +276,9 @@ impl ObjectImpl for ProviderPriv {
subclass::Property("name", ..) => self.name.borrow().to_value(),
subclass::Property("period", ..) => self.period.get().to_value(),
subclass::Property("algorithm", ..) => self.algorithm.borrow().to_value(),
subclass::Property("digits", ..) => self.digits.get().to_value(),
subclass::Property("hmac-algorithm", ..) => self.hmac_algorithm.borrow().to_value(),
subclass::Property("default-counter", ..) => self.default_counter.get().to_value(),
subclass::Property("website", ..) => self.website.borrow().to_value(),
subclass::Property("help-url", ..) => self.help_url.borrow().to_value(),
subclass::Property("image-uri", ..) => self.image_uri.borrow().to_value(),
@ -232,6 +297,9 @@ impl Provider {
period: i32,
algorithm: Algorithm,
website: Option<String>,
hmac_algorithm: HOTPAlgorithm,
digits: i32,
default_counter: i32,
) -> Result<Self> {
use crate::diesel::ExpressionMethods;
let db = database::connection();
@ -243,6 +311,9 @@ impl Provider {
period,
algorithm: algorithm.to_string(),
website,
hmac_algorithm: hmac_algorithm.to_string(),
digits,
default_counter,
help_url: None,
image_uri: None,
})
@ -285,6 +356,9 @@ impl Provider {
name: &str,
period: i32,
algorithm: Algorithm,
hmac_algorithm: HOTPAlgorithm,
digits: i32,
default_counter: i32,
website: Option<String>,
help_url: Option<String>,
image_uri: Option<String>,
@ -299,6 +373,9 @@ impl Provider {
("image-uri", &image_uri),
("period", &period),
("algorithm", &algorithm.to_string()),
("hmac-algorithm", &hmac_algorithm.to_string()),
("digits", &digits),
("default-counter", &default_counter),
],
)
.expect("Failed to create provider")
@ -342,11 +419,26 @@ impl Provider {
priv_.name.borrow().clone()
}
pub fn digits(&self) -> i32 {
let priv_ = ProviderPriv::from_instance(self);
priv_.digits.get()
}
pub fn default_counter(&self) -> i32 {
let priv_ = ProviderPriv::from_instance(self);
priv_.default_counter.get()
}
pub fn period(&self) -> i32 {
let priv_ = ProviderPriv::from_instance(self);
priv_.period.get()
}
pub fn hmac_algorithm(&self) -> HOTPAlgorithm {
let priv_ = ProviderPriv::from_instance(self);
HOTPAlgorithm::from_str(&priv_.hmac_algorithm.borrow().clone()).unwrap()
}
pub fn algorithm(&self) -> Algorithm {
let priv_ = ProviderPriv::from_instance(self);
Algorithm::from_str(&priv_.algorithm.borrow().clone()).unwrap()
@ -417,6 +509,9 @@ impl From<DiProvider> for Provider {
&p.name,
p.period,
Algorithm::from_str(&p.algorithm).unwrap(),
HOTPAlgorithm::from_str(&p.hmac_algorithm).unwrap(),
p.digits,
p.default_counter,
p.website,
p.help_url,
p.image_uri,
@ -431,6 +526,9 @@ impl From<&Provider> for DiProvider {
name: p.name(),
period: p.period(),
algorithm: p.algorithm().to_string(),
hmac_algorithm: p.hmac_algorithm().to_string(),
digits: p.digits(),
default_counter: p.default_counter(),
website: p.website(),
help_url: p.help_url(),
image_uri: p.image_uri(),

View file

@ -2,6 +2,7 @@ table! {
accounts (id) {
id -> Integer,
name -> Text,
counter -> Integer,
token_id -> Text,
provider_id -> Integer,
}
@ -11,11 +12,14 @@ table! {
providers (id) {
id -> Integer,
name -> Text,
period -> Integer,
algorithm -> Text,
website -> Nullable<Text>,
help_url -> Nullable<Text>,
image_uri -> Nullable<Text>,
period -> Integer,
digits -> Integer,
default_counter -> Integer,
hmac_algorithm -> Text,
algorithm -> Text,
}
}

View file

@ -1,6 +1,6 @@
use crate::application::Action;
use crate::helpers::{qrcode, Keyring};
use crate::models::{Account, Provider, ProvidersModel};
use crate::models::{Account, Algorithm, Provider, ProvidersModel};
use anyhow::Result;
use gio::prelude::*;
use gio::{subclass::ObjectSubclass, ActionMapExt};
@ -42,6 +42,9 @@ mod imp {
#[template_child(id = "period_label")]
pub period_label: TemplateChild<gtk::Label>,
#[template_child(id = "digits_label")]
pub digits_label: TemplateChild<gtk::Label>,
#[template_child(id = "provider_entry")]
pub provider_entry: TemplateChild<gtk::Entry>,
@ -54,6 +57,15 @@ mod imp {
#[template_child(id = "provider_help_row")]
pub provider_help_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "hmac_algorithm_row")]
pub hmac_algorithm_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "counter_row")]
pub counter_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "period_row")]
pub period_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "provider_completion")]
pub provider_completion: TemplateChild<gtk::EntryCompletion>,
@ -93,11 +105,15 @@ mod imp {
username_entry: TemplateChild::default(),
more_list: TemplateChild::default(),
period_label: TemplateChild::default(),
digits_label: TemplateChild::default(),
provider_entry: TemplateChild::default(),
algorithm_label: TemplateChild::default(),
provider_website_row: TemplateChild::default(),
provider_help_row: TemplateChild::default(),
provider_completion: TemplateChild::default(),
hmac_algorithm_row: TemplateChild::default(),
counter_row: TemplateChild::default(),
period_row: TemplateChild::default(),
image: TemplateChild::default(),
spinner: TemplateChild::default(),
image_stack: TemplateChild::default(),
@ -229,6 +245,24 @@ impl AccountAddDialog {
.get()
.set_text(&provider.algorithm().to_locale_string());
self_
.digits_label
.get()
.set_text(&provider.digits().to_string());
match provider.algorithm() {
Algorithm::TOTP => {
self_.hmac_algorithm_row.get().hide();
self_.counter_row.get().hide();
self_.period_row.get().show();
}
Algorithm::HOTP => {
self_.hmac_algorithm_row.get().show();
self_.counter_row.get().show();
self_.period_row.get().hide();
}
Algorithm::Steam => {}
};
if let Some(ref website) = provider.website() {
self_.provider_website_row.get().set_subtitle(Some(website));
}
@ -291,6 +325,7 @@ impl AccountAddDialog {
.provider_completion
.get()
.set_model(Some(&self_.model.get().unwrap().completion_model()));
self_.provider_completion.get().connect_match_selected(
clone!(@strong self as dialog, @strong self_.model as model => move |_, store, iter| {
let provider_id = store.get_value(iter, 0). get_some::<i32>().unwrap();

View file

@ -1,4 +1,4 @@
use crate::models::{Algorithm, Provider};
use crate::models::{Algorithm, HOTPAlgorithm, Provider};
use gio::subclass::ObjectSubclass;
use glib::subclass::prelude::*;
use glib::translate::ToGlib;
@ -22,6 +22,10 @@ mod imp {
pub name_entry: TemplateChild<gtk::Entry>,
#[template_child(id = "period_spinbutton")]
pub period_spinbutton: TemplateChild<gtk::SpinButton>,
#[template_child(id = "digits_spinbutton")]
pub digits_spinbutton: TemplateChild<gtk::SpinButton>,
#[template_child(id = "default_counter_spinbutton")]
pub default_counter_spinbutton: TemplateChild<gtk::SpinButton>,
#[template_child(id = "provider_website_entry")]
pub provider_website_entry: TemplateChild<gtk::Entry>,
#[template_child(id = "provider_help_entry")]
@ -32,9 +36,18 @@ mod imp {
pub spinner: TemplateChild<gtk::Spinner>,
#[template_child(id = "algorithm_comborow")]
pub algorithm_comborow: TemplateChild<libhandy::ComboRow>,
#[template_child(id = "hmac_algorithm_comborow")]
pub hmac_algorithm_comborow: TemplateChild<libhandy::ComboRow>,
#[template_child(id = "period_row")]
pub period_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "digits_row")]
pub digits_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "default_counter_row")]
pub default_counter_row: TemplateChild<libhandy::ActionRow>,
#[template_child(id = "title")]
pub title: TemplateChild<gtk::Label>,
pub algorithms_model: libhandy::EnumListModel,
pub hmac_algorithms_model: libhandy::EnumListModel,
}
impl ObjectSubclass for ProviderPage {
@ -48,17 +61,25 @@ mod imp {
fn new() -> Self {
let algorithms_model = libhandy::EnumListModel::new(Algorithm::static_type());
let hmac_algorithms_model = libhandy::EnumListModel::new(HOTPAlgorithm::static_type());
Self {
name_entry: TemplateChild::default(),
period_spinbutton: TemplateChild::default(),
digits_spinbutton: TemplateChild::default(),
default_counter_spinbutton: TemplateChild::default(),
provider_website_entry: TemplateChild::default(),
provider_help_entry: TemplateChild::default(),
image_stack: TemplateChild::default(),
spinner: TemplateChild::default(),
algorithm_comborow: TemplateChild::default(),
hmac_algorithm_comborow: TemplateChild::default(),
period_row: TemplateChild::default(),
digits_row: TemplateChild::default(),
default_counter_row: TemplateChild::default(),
title: TemplateChild::default(),
algorithms_model,
hmac_algorithms_model,
}
}
@ -114,6 +135,22 @@ impl ProviderPage {
.algorithms_model
.find_position(provider.algorithm().to_glib()),
);
self.on_algorithm_changed();
self_
.default_counter_spinbutton
.get()
.set_value(provider.default_counter() as f64);
self_
.digits_spinbutton
.get()
.set_value(provider.digits() as f64);
self_.hmac_algorithm_comborow.get().set_selected(
self_
.hmac_algorithms_model
.find_position(provider.hmac_algorithm().to_glib()),
);
/*let sender = self.sender.clone();
spawn!(async move {
@ -134,6 +171,40 @@ impl ProviderPage {
.algorithm_comborow
.get()
.set_model(Some(&self_.algorithms_model));
let default_counter_row = self_.default_counter_row.get();
let hmac_algorithm_comborow = self_.hmac_algorithm_comborow.get();
let period_row = self_.period_row.get();
self_
.algorithm_comborow
.get()
.connect_property_selected_item_notify(clone!(@weak self as page => move |_| {
page.on_algorithm_changed();
}));
self_
.hmac_algorithm_comborow
.get()
.set_model(Some(&self_.hmac_algorithms_model));
}
fn on_algorithm_changed(&self) {
let self_ = imp::ProviderPage::from_instance(self);
let selected = Algorithm::from(self_.algorithm_comborow.get().get_selected());
match selected {
Algorithm::TOTP => {
self_.default_counter_row.get().hide();
self_.hmac_algorithm_comborow.get().hide();
self_.period_row.get().show();
}
Algorithm::HOTP => {
self_.default_counter_row.get().show();
self_.hmac_algorithm_comborow.get().show();
self_.period_row.get().hide();
}
Algorithm::Steam => {}
}
}
pub fn set_mode(&self, mode: ProviderPageMode) {

View file

@ -57,8 +57,8 @@ for db_file in glob(DATA_DIR + "/*.yml"):
name = provider.get("name").replace("&amp;", "&")
website = provider.get("url", "")
help_url = provider.get("doc", "")
up_query += f"INSERT INTO `providers` (`name`, `website`, `help_url`) VALUES (`{name}`, `{website}`, `{help_url}`);\n"
down_query += f"DELETE FROM `providers` WHERE `name`=`{name}`;\n"
up_query += f'INSERT INTO "providers" ("name", "website", "help_url") VALUES ("{name}", "{website}", "{help_url}");\n'
down_query += f'DELETE FROM "providers" WHERE "name"="{name}";\n'
except (yaml.YAMLError, TypeError, KeyError) as error:
pass