mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Add a way to log out
This commit is contained in:
parent
67fad1329d
commit
5b066a05b6
6 changed files with 126 additions and 35 deletions
|
|
@ -10,15 +10,15 @@ use std::rc::Rc;
|
||||||
|
|
||||||
/// A dialog for entering login credentials.
|
/// A dialog for entering login credentials.
|
||||||
pub struct LoginDialog {
|
pub struct LoginDialog {
|
||||||
handle: NavigationHandle<LoginData>,
|
handle: NavigationHandle<Option<LoginData>>,
|
||||||
widget: gtk::Stack,
|
widget: gtk::Stack,
|
||||||
info_bar: gtk::InfoBar,
|
info_bar: gtk::InfoBar,
|
||||||
username_entry: gtk::Entry,
|
username_entry: gtk::Entry,
|
||||||
password_entry: gtk::Entry,
|
password_entry: gtk::Entry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen<(), LoginData> for LoginDialog {
|
impl Screen<Option<LoginData>, Option<LoginData>> for LoginDialog {
|
||||||
fn new(_: (), handle: NavigationHandle<LoginData>) -> Rc<Self> {
|
fn new(data: Option<LoginData>, handle: NavigationHandle<Option<LoginData>>) -> Rc<Self> {
|
||||||
// Create UI
|
// Create UI
|
||||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/login_dialog.ui");
|
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/login_dialog.ui");
|
||||||
|
|
||||||
|
|
@ -28,7 +28,16 @@ impl Screen<(), LoginData> for LoginDialog {
|
||||||
get_widget!(builder, gtk::Button, login_button);
|
get_widget!(builder, gtk::Button, login_button);
|
||||||
get_widget!(builder, gtk::Entry, username_entry);
|
get_widget!(builder, gtk::Entry, username_entry);
|
||||||
get_widget!(builder, gtk::Entry, password_entry);
|
get_widget!(builder, gtk::Entry, password_entry);
|
||||||
|
get_widget!(builder, gtk::Box, register_box);
|
||||||
get_widget!(builder, gtk::Button, register_button);
|
get_widget!(builder, gtk::Button, register_button);
|
||||||
|
get_widget!(builder, gtk::Box, logout_box);
|
||||||
|
get_widget!(builder, gtk::Button, logout_button);
|
||||||
|
|
||||||
|
if let Some(data) = data {
|
||||||
|
username_entry.set_text(&data.username);
|
||||||
|
register_box.hide();
|
||||||
|
logout_box.show();
|
||||||
|
}
|
||||||
|
|
||||||
let this = Rc::new(Self {
|
let this = Rc::new(Self {
|
||||||
handle,
|
handle,
|
||||||
|
|
@ -53,9 +62,9 @@ impl Screen<(), LoginData> for LoginDialog {
|
||||||
};
|
};
|
||||||
|
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
this.handle.backend.set_login_data(data.clone()).await;
|
this.handle.backend.set_login_data(Some(data.clone())).await;
|
||||||
if this.handle.backend.cl().login().await.unwrap() {
|
if this.handle.backend.cl().login().await.unwrap() {
|
||||||
this.handle.pop(Some(data));
|
this.handle.pop(Some(Some(data)));
|
||||||
} else {
|
} else {
|
||||||
this.widget.set_visible_child_name("content");
|
this.widget.set_visible_child_name("content");
|
||||||
this.info_bar.set_revealed(true);
|
this.info_bar.set_revealed(true);
|
||||||
|
|
@ -66,11 +75,18 @@ impl Screen<(), LoginData> for LoginDialog {
|
||||||
register_button.connect_clicked(clone!(@weak this => move |_| {
|
register_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
if let Some(data) = push!(this.handle, RegisterDialog).await {
|
if let Some(data) = push!(this.handle, RegisterDialog).await {
|
||||||
this.handle.pop(Some(data));
|
this.handle.pop(Some(Some(data)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
logout_button.connect_clicked(clone!(@weak this => move |_| {
|
||||||
|
spawn!(@clone this, async move {
|
||||||
|
this.handle.backend.set_login_data(None).await;
|
||||||
|
this.handle.pop(Some(None));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,12 @@ impl Preferences {
|
||||||
window.set_transient_for(&this.window);
|
window.set_transient_for(&this.window);
|
||||||
|
|
||||||
spawn!(@clone this, async move {
|
spawn!(@clone this, async move {
|
||||||
if let Some(data) = replace!(window.navigator, LoginDialog).await {
|
if let Some(data) = replace!(window.navigator, LoginDialog, this.backend.get_login_data()).await {
|
||||||
this.login_row.set_subtitle(Some(&data.username));
|
if let Some(data) = data {
|
||||||
|
this.login_row.set_subtitle(Some(&data.username));
|
||||||
|
} else {
|
||||||
|
this.login_row.set_subtitle(Some(&gettext("Not logged in")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ impl Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
match Self::load_login_data().await {
|
match Self::load_login_data().await {
|
||||||
Ok(Some(data)) => self.client.set_login_data(data),
|
Ok(Some(data)) => self.client.set_login_data(Some(data)),
|
||||||
Err(err) => warn!("The login data could not be loaded from SecretService. It will not \
|
Err(err) => warn!("The login data could not be loaded from SecretService. It will not \
|
||||||
be available. Error message: {}", err),
|
be available. Error message: {}", err),
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
@ -129,11 +129,19 @@ impl Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the user credentials to use.
|
/// Set the user credentials to use.
|
||||||
pub async fn set_login_data(&self, data: LoginData) {
|
pub async fn set_login_data(&self, data: Option<LoginData>) {
|
||||||
if let Err(err) = Self::store_login_data(data.clone()).await {
|
if let Some(data) = &data {
|
||||||
warn!("An error happened while trying to store the login data using SecretService. \
|
if let Err(err) = Self::store_login_data(data.clone()).await {
|
||||||
This means, that they will not be available at the next startup most likely. \
|
warn!("An error happened while trying to store the login data using SecretService. \
|
||||||
Error message: {}", err);
|
This means, that they will not be available at the next startup most likely. \
|
||||||
|
Error message: {}", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(err) = Self::delete_secrets().await {
|
||||||
|
warn!("An error happened while trying to delete the login data from SecretService. \
|
||||||
|
This may result in the login data being reloaded at the next startup. Error \
|
||||||
|
message: {}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.client.set_login_data(data);
|
self.client.set_login_data(data);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,13 @@ impl Backend {
|
||||||
receiver.await?
|
receiver.await?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete all stored secrets.
|
||||||
|
pub(super) async fn delete_secrets() -> Result<()> {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
thread::spawn(move || sender.send(Self::delete_secrets_priv()).unwrap());
|
||||||
|
receiver.await?
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the login credentials from secret storage.
|
/// Get the login credentials from secret storage.
|
||||||
fn load_login_data_priv() -> Result<Option<LoginData>> {
|
fn load_login_data_priv() -> Result<Option<LoginData>> {
|
||||||
let ss = SecretService::new(EncryptionType::Dh)?;
|
let ss = SecretService::new(EncryptionType::Dh)?;
|
||||||
|
|
@ -52,7 +59,7 @@ impl Backend {
|
||||||
let collection = Self::get_collection(&ss)?;
|
let collection = Self::get_collection(&ss)?;
|
||||||
|
|
||||||
let key = "musicus-login-data";
|
let key = "musicus-login-data";
|
||||||
Self::delete_secrets(&collection, key)?;
|
Self::delete_secrets_for_key(&collection, key)?;
|
||||||
|
|
||||||
let mut attributes = HashMap::new();
|
let mut attributes = HashMap::new();
|
||||||
attributes.insert("username", data.username.as_str());
|
attributes.insert("username", data.username.as_str());
|
||||||
|
|
@ -61,8 +68,19 @@ impl Backend {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Delete all stored secrets.
|
||||||
|
fn delete_secrets_priv() -> Result<()> {
|
||||||
|
let ss = SecretService::new(EncryptionType::Dh)?;
|
||||||
|
let collection = Self::get_collection(&ss)?;
|
||||||
|
|
||||||
|
let key = "musicus-login-data";
|
||||||
|
Self::delete_secrets_for_key(&collection, key)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete all stored secrets for the provided key.
|
/// Delete all stored secrets for the provided key.
|
||||||
fn delete_secrets(collection: &Collection, key: &str) -> Result<()> {
|
fn delete_secrets_for_key(collection: &Collection, key: &str) -> Result<()> {
|
||||||
let items = collection.get_all_items()?;
|
let items = collection.get_all_items()?;
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
|
|
|
||||||
|
|
@ -65,8 +65,8 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the user credentials to use.
|
/// Set the user credentials to use.
|
||||||
pub fn set_login_data(&self, data: LoginData) {
|
pub fn set_login_data(&self, data: Option<LoginData>) {
|
||||||
self.login_data.replace(Some(data));
|
self.login_data.replace(data);
|
||||||
self.token.replace(None);
|
self.token.replace(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,29 +100,74 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel">
|
<object class="GtkBox" id="register_box">
|
||||||
<property name="halign">start</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="label" translatable="yes">Create a new account</property>
|
<property name="spacing">12</property>
|
||||||
<attributes>
|
<child>
|
||||||
<attribute name="weight" value="bold"/>
|
<object class="GtkLabel">
|
||||||
</attributes>
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Create a new account</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="bold"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkFrame">
|
||||||
|
<property name="valign">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkListBox">
|
||||||
|
<property name="selection-mode">none</property>
|
||||||
|
<child>
|
||||||
|
<object class="AdwActionRow">
|
||||||
|
<property name="activatable">True</property>
|
||||||
|
<property name="title" translatable="yes">Register a new account</property>
|
||||||
|
<property name="activatable-widget">register_button</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="register_button">
|
||||||
|
<property name="label" translatable="yes">Start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkFrame">
|
<object class="GtkBox" id="logout_box">
|
||||||
<property name="valign">start</property>
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<property name="visible">false</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkListBox">
|
<object class="GtkLabel">
|
||||||
<property name="selection-mode">none</property>
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Logout</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="weight" value="bold"/>
|
||||||
|
</attributes>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkFrame">
|
||||||
|
<property name="valign">start</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow">
|
<object class="GtkListBox">
|
||||||
<property name="activatable">True</property>
|
<property name="selection-mode">none</property>
|
||||||
<property name="title" translatable="yes">Register a new account</property>
|
|
||||||
<property name="activatable-widget">register_button</property>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="register_button">
|
<object class="AdwActionRow">
|
||||||
<property name="label" translatable="yes">Start</property>
|
<property name="activatable">True</property>
|
||||||
<property name="valign">center</property>
|
<property name="title" translatable="yes">Don't use an account</property>
|
||||||
|
<property name="activatable-widget">logout_button</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="logout_button">
|
||||||
|
<property name="label" translatable="yes">Logout</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue