add the possiblity to clear password & properly sync state

This commit is contained in:
Bilal Elmoussaoui 2020-11-10 13:49:45 +01:00
parent 2068721006
commit 6e1f81b929
4 changed files with 114 additions and 33 deletions

View file

@ -107,6 +107,20 @@
</style> </style>
</object> </object>
</child> </child>
<child>
<object class="GtkButton">
<property name="action-name">preferences.reset_password</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="label">Reset</property>
<property name="margin-end">8</property>
<style>
<class name="destructive-action" />
<class name="large-button" />
<class name="pill-button" />
</style>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>

View file

@ -28,10 +28,22 @@ pub struct ApplicationPrivate {
sender: Sender<Action>, sender: Sender<Action>,
receiver: RefCell<Option<Receiver<Action>>>, receiver: RefCell<Option<Receiver<Action>>>,
locked: Cell<bool>, locked: Cell<bool>,
can_be_locked: Cell<bool>,
} }
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("locked", |name| { static PROPERTIES: [subclass::Property; 2] = [
glib::ParamSpec::boolean(name, "locked", "locked", false, glib::ParamFlags::READWRITE) 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 { impl ObjectSubclass for ApplicationPrivate {
const NAME: &'static str = "Application"; const NAME: &'static str = "Application";
type ParentType = gtk::Application; type ParentType = gtk::Application;
@ -54,6 +66,7 @@ impl ObjectSubclass for ApplicationPrivate {
sender, sender,
receiver, receiver,
model, model,
can_be_locked: Cell::new(false),
locked: Cell::new(false), locked: Cell::new(false),
} }
} }
@ -71,6 +84,13 @@ impl ObjectImpl for ApplicationPrivate {
.unwrap(); .unwrap();
self.locked.set(locked); 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!(), _ => unimplemented!(),
} }
} }
@ -80,6 +100,7 @@ impl ObjectImpl for ApplicationPrivate {
match *prop { match *prop {
subclass::Property("locked", ..) => Ok(self.locked.get().to_value()), subclass::Property("locked", ..) => Ok(self.locked.get().to_value()),
subclass::Property("can-be-locked", ..) => Ok(self.can_be_locked.get().to_value()),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
@ -140,6 +161,10 @@ impl ApplicationImpl for ApplicationPrivate {
app_.set_locked(true); 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) { fn activate(&self, _app: &gio::Application) {
@ -154,16 +179,16 @@ impl ApplicationImpl for ApplicationPrivate {
let window = app.create_window(); let window = app.create_window();
window.present(); window.present();
self.window.replace(Some(window)); 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_resource_base_path(Some("/com/belmoussaoui/Authenticator"));
app.set_accels_for_action("app.quit", &["<primary>q"]); app.set_accels_for_action("app.quit", &["<primary>q"]);
app.set_accels_for_action("app.lock", &["<primary>l"]);
app.set_accels_for_action("win.show-help-overlay", &["<primary>question"]); app.set_accels_for_action("win.show-help-overlay", &["<primary>question"]);
app.set_accels_for_action("win.search", &["<primary>f"]); app.set_accels_for_action("win.search", &["<primary>f"]);
app.set_accels_for_action("win.add-account", &["<primary>n"]); app.set_accels_for_action("win.add-account", &["<primary>n"]);
app.set_property( app.set_locked(has_set_password);
"locked", app.set_can_be_locked(has_set_password);
&Keyring::has_set_password().unwrap_or_else(|_| false),
);
let receiver = self.receiver.borrow_mut().take().unwrap(); let receiver = self.receiver.borrow_mut().take().unwrap();
receiver.attach(None, move |action| app.do_action(action)); receiver.attach(None, move |action| app.do_action(action));
} }
@ -210,6 +235,10 @@ impl Application {
self.set_property("locked", &state); 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 { fn create_window(&self) -> Window {
let self_ = ApplicationPrivate::from_instance(self); let self_ = ApplicationPrivate::from_instance(self);
let window = Window::new(self_.model.clone(), self_.sender.clone(), &self.clone()); let window = Window::new(self_.model.clone(), self_.sender.clone(), &self.clone());

View file

@ -40,6 +40,18 @@ impl Keyring {
Ok(()) 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<bool, SsError> { pub fn is_current_password(password: &str) -> Result<bool, SsError> {
let ss = SecretService::new(EncryptionType::Dh)?; let ss = SecretService::new(EncryptionType::Dh)?;
let col = Self::get_default_collection(&ss)?; let col = Self::get_default_collection(&ss)?;

View file

@ -1,11 +1,14 @@
use crate::helpers::Keyring; use crate::helpers::Keyring;
use gio::{ActionExt, ActionMapExt}; use gio::{ActionExt, ActionMapExt};
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
pub struct PasswordPage { pub struct PasswordPage {
pub widget: gtk::Box, pub widget: gtk::Box,
builder: gtk::Builder, builder: gtk::Builder,
actions: gio::SimpleActionGroup, actions: gio::SimpleActionGroup,
has_set_password: Cell<bool>,
} }
impl PasswordPage { impl PasswordPage {
@ -14,50 +17,51 @@ impl PasswordPage {
"/com/belmoussaoui/Authenticator/preferences_password_page.ui", "/com/belmoussaoui/Authenticator/preferences_password_page.ui",
); );
get_widget!(builder, gtk::Box, password_page); get_widget!(builder, gtk::Box, password_page);
let has_set_password = Keyring::has_set_password().unwrap_or_else(|_| false);
let page = Rc::new(Self { let page = Rc::new(Self {
widget: password_page, widget: password_page,
builder, builder,
actions, actions,
has_set_password: Cell::new(has_set_password),
}); });
page.init(page.clone()); page.init(page.clone());
page 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<Self>) { fn init(&self, page: Rc<Self>) {
get_widget!(self.builder, gtk::PasswordEntry, current_password_entry); get_widget!(self.builder, gtk::PasswordEntry, current_password_entry);
get_widget!(self.builder, gtk::PasswordEntry, password_entry); get_widget!(self.builder, gtk::PasswordEntry, password_entry);
get_widget!(self.builder, gtk::PasswordEntry, confirm_password_entry); get_widget!(self.builder, gtk::PasswordEntry, confirm_password_entry);
get_widget!(self.builder, libhandy::ActionRow, current_password_row); 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, if !self.has_set_password.get() {
@weak current_password_entry,
@weak password_entry,
@weak self.actions as actions,
@weak confirm_password_entry => move |_: &gtk::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 {
current_password_row.hide(); current_password_row.hide();
} else { } else {
current_password_entry.connect_changed(validate.clone()); current_password_entry
.connect_changed(clone!(@strong page => move |_| page.validate()));
} }
action!( action!(
@ -67,7 +71,27 @@ impl PasswordPage {
page.save(); 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, @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) { fn save(&self) {
@ -90,7 +114,9 @@ impl PasswordPage {
password_entry.set_text(""); password_entry.set_text("");
confirm_password_entry.set_text(""); confirm_password_entry.set_text("");
get_action!(self.actions, @save_password).set_enabled(false); 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); get_action!(self.actions, @close_page).activate(None);
self.has_set_password.set(true);
} }
} }
} }