From 6e1f81b929e0fac20e69ef33f0258c2942cd6f5d Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 10 Nov 2020 13:49:45 +0100 Subject: [PATCH] add the possiblity to clear password & properly sync state --- .../ui/preferences_password_page.ui.in | 14 ++++ src/application.rs | 43 ++++++++-- src/helpers/keyring.rs | 12 +++ src/widgets/preferences/password_page.rs | 78 ++++++++++++------- 4 files changed, 114 insertions(+), 33 deletions(-) diff --git a/data/resources/ui/preferences_password_page.ui.in b/data/resources/ui/preferences_password_page.ui.in index c31b66e..2970b66 100644 --- a/data/resources/ui/preferences_password_page.ui.in +++ b/data/resources/ui/preferences_password_page.ui.in @@ -107,6 +107,20 @@ + + + preferences.reset_password + end + center + Reset + 8 + + + diff --git a/src/application.rs b/src/application.rs index ca6ddc7..c2a897f 100644 --- a/src/application.rs +++ b/src/application.rs @@ -28,10 +28,22 @@ pub struct ApplicationPrivate { sender: Sender, receiver: RefCell>>, locked: Cell, + can_be_locked: Cell, } -static PROPERTIES: [subclass::Property; 1] = [subclass::Property("locked", |name| { - glib::ParamSpec::boolean(name, "locked", "locked", false, glib::ParamFlags::READWRITE) -})]; +static PROPERTIES: [subclass::Property; 2] = [ + subclass::Property("locked", |name| { + glib::ParamSpec::boolean(name, "locked", "locked", false, glib::ParamFlags::READWRITE) + }), + subclass::Property("can-be-locked", |name| { + glib::ParamSpec::boolean( + name, + "can_be_locked", + "can be locked", + false, + glib::ParamFlags::READWRITE, + ) + }), +]; impl ObjectSubclass for ApplicationPrivate { const NAME: &'static str = "Application"; type ParentType = gtk::Application; @@ -54,6 +66,7 @@ impl ObjectSubclass for ApplicationPrivate { sender, receiver, model, + can_be_locked: Cell::new(false), locked: Cell::new(false), } } @@ -71,6 +84,13 @@ impl ObjectImpl for ApplicationPrivate { .unwrap(); self.locked.set(locked); } + subclass::Property("can-be-locked", ..) => { + let can_be_locked = value + .get() + .expect("type conformity checked by `Object::set_property`") + .unwrap(); + self.can_be_locked.set(can_be_locked); + } _ => unimplemented!(), } } @@ -80,6 +100,7 @@ impl ObjectImpl for ApplicationPrivate { match *prop { subclass::Property("locked", ..) => Ok(self.locked.get().to_value()), + subclass::Property("can-be-locked", ..) => Ok(self.can_be_locked.get().to_value()), _ => unimplemented!(), } } @@ -140,6 +161,10 @@ impl ApplicationImpl for ApplicationPrivate { app_.set_locked(true); }) ); + application + .bind_property("can-be-locked", &get_action!(application, @lock), "enabled") + .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE) + .build(); } fn activate(&self, _app: &gio::Application) { @@ -154,16 +179,16 @@ impl ApplicationImpl for ApplicationPrivate { let window = app.create_window(); window.present(); self.window.replace(Some(window)); + let has_set_password = Keyring::has_set_password().unwrap_or_else(|_| false); app.set_resource_base_path(Some("/com/belmoussaoui/Authenticator")); app.set_accels_for_action("app.quit", &["q"]); + app.set_accels_for_action("app.lock", &["l"]); app.set_accels_for_action("win.show-help-overlay", &["question"]); app.set_accels_for_action("win.search", &["f"]); app.set_accels_for_action("win.add-account", &["n"]); - app.set_property( - "locked", - &Keyring::has_set_password().unwrap_or_else(|_| false), - ); + app.set_locked(has_set_password); + app.set_can_be_locked(has_set_password); let receiver = self.receiver.borrow_mut().take().unwrap(); receiver.attach(None, move |action| app.do_action(action)); } @@ -210,6 +235,10 @@ impl Application { self.set_property("locked", &state); } + pub fn set_can_be_locked(&self, state: bool) { + self.set_property("can-be-locked", &state); + } + fn create_window(&self) -> Window { let self_ = ApplicationPrivate::from_instance(self); let window = Window::new(self_.model.clone(), self_.sender.clone(), &self.clone()); diff --git a/src/helpers/keyring.rs b/src/helpers/keyring.rs index bc0e04b..f91c54a 100644 --- a/src/helpers/keyring.rs +++ b/src/helpers/keyring.rs @@ -40,6 +40,18 @@ impl Keyring { Ok(()) } + pub fn reset_password() -> Result<(), SsError> { + let ss = SecretService::new(EncryptionType::Dh)?; + let col = Self::get_default_collection(&ss)?; + let items = + col.search_items(vec![("type", "password"), ("application", config::APP_ID)])?; + + match items.get(0) { + Some(i) => i.delete(), + None => Err(SsError::NoResult), + } + } + pub fn is_current_password(password: &str) -> Result { let ss = SecretService::new(EncryptionType::Dh)?; let col = Self::get_default_collection(&ss)?; diff --git a/src/widgets/preferences/password_page.rs b/src/widgets/preferences/password_page.rs index f8d4453..264d1a4 100644 --- a/src/widgets/preferences/password_page.rs +++ b/src/widgets/preferences/password_page.rs @@ -1,11 +1,14 @@ use crate::helpers::Keyring; use gio::{ActionExt, ActionMapExt}; use gtk::prelude::*; +use std::cell::Cell; use std::rc::Rc; + pub struct PasswordPage { pub widget: gtk::Box, builder: gtk::Builder, actions: gio::SimpleActionGroup, + has_set_password: Cell, } impl PasswordPage { @@ -14,50 +17,51 @@ impl PasswordPage { "/com/belmoussaoui/Authenticator/preferences_password_page.ui", ); get_widget!(builder, gtk::Box, password_page); + + let has_set_password = Keyring::has_set_password().unwrap_or_else(|_| false); + let page = Rc::new(Self { widget: password_page, builder, actions, + has_set_password: Cell::new(has_set_password), }); page.init(page.clone()); page } + fn validate(&self) { + get_widget!(self.builder, gtk::PasswordEntry, current_password_entry); + get_widget!(self.builder, gtk::PasswordEntry, password_entry); + get_widget!(self.builder, gtk::PasswordEntry, confirm_password_entry); + + let current_password = current_password_entry.get_text().unwrap(); + let password = password_entry.get_text().unwrap(); + let password_repeat = confirm_password_entry.get_text().unwrap(); + + let is_valid = if self.has_set_password.get() { + password_repeat == password && current_password != password && password != "" + } else { + password_repeat == password && password != "" + }; + + get_action!(self.actions, @save_password).set_enabled(is_valid); + } + fn init(&self, page: Rc) { get_widget!(self.builder, gtk::PasswordEntry, current_password_entry); get_widget!(self.builder, gtk::PasswordEntry, password_entry); get_widget!(self.builder, gtk::PasswordEntry, confirm_password_entry); get_widget!(self.builder, libhandy::ActionRow, current_password_row); - let has_set_password = Keyring::has_set_password().unwrap_or_else(|_| false); + password_entry.connect_changed(clone!(@strong page => move |_| page.validate())); + confirm_password_entry.connect_changed(clone!(@strong page => move |_| page.validate())); - let validate = clone!(@strong self.builder as builder, - @weak current_password_entry, - @weak password_entry, - @weak self.actions as actions, - @weak confirm_password_entry => move |_: >k::PasswordEntry| { - - let current_password = current_password_entry.get_text().unwrap(); - let password = password_entry.get_text().unwrap(); - let password_repeat = confirm_password_entry.get_text().unwrap(); - - let is_valid = if has_set_password { - password_repeat == password && current_password != password - && password != "" - } else { - password_repeat == password && password != "" - }; - - get_action!(actions, @save_password).set_enabled(is_valid); - }); - - password_entry.connect_changed(validate.clone()); - confirm_password_entry.connect_changed(validate.clone()); - - if !has_set_password { + if !self.has_set_password.get() { current_password_row.hide(); } else { - current_password_entry.connect_changed(validate.clone()); + current_password_entry + .connect_changed(clone!(@strong page => move |_| page.validate())); } action!( @@ -67,7 +71,27 @@ impl PasswordPage { page.save(); }) ); + + action!( + self.actions, + "reset_password", + clone!(@strong page => move |_,_| { + page.reset(); + }) + ); + get_action!(self.actions, @save_password).set_enabled(false); + get_action!(self.actions, @reset_password).set_enabled(self.has_set_password.get()); + } + + fn reset(&self) { + if Keyring::reset_password().is_ok() { + get_action!(self.actions, @close_page).activate(None); + get_action!(self.actions, @save_password).set_enabled(false); + get_action!(self.actions, @reset_password).set_enabled(false); + get_widget!(self.builder, libhandy::ActionRow, @current_password_row).hide(); + self.has_set_password.set(false); + } } fn save(&self) { @@ -90,7 +114,9 @@ impl PasswordPage { password_entry.set_text(""); confirm_password_entry.set_text(""); get_action!(self.actions, @save_password).set_enabled(false); + get_action!(self.actions, @reset_password).set_enabled(true); get_action!(self.actions, @close_page).activate(None); + self.has_set_password.set(true); } } }