Refactoring the login-related code.

This commit is contained in:
Werner Kroneman 2023-12-10 18:11:21 +01:00
parent 74e2cfd6b7
commit f4e8d5fa3b
3 changed files with 66 additions and 49 deletions

View File

@ -14,11 +14,12 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::passwords::Password; use crate::passwords::{Password, try_retrieve_password_from_keyring};
use jid::BareJid; use jid::BareJid;
use keyring::Entry; use keyring::Entry;
use log::error; use log::{error, info};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::types::LoginCredentials;
/// The configuration struct containing all the configuration options. /// The configuration struct containing all the configuration options.
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Default, Debug, Serialize, Deserialize)]
@ -70,3 +71,22 @@ pub fn load_config() -> Configuration {
} }
} }
} }
/// Retrieve the full login credentials from the config and keyring, if they exist.
pub fn try_retrieve_credentials(config: &Configuration) -> Option<LoginCredentials> {
if let (Some(user), Some(nick)) = (&config.stored_username, &config.stored_default_nick) {
if let Some(password) = try_retrieve_password_from_keyring(&user) {
Some(LoginCredentials {
username: user.clone(),
default_nick: nick.clone(),
password,
})
} else {
info!("No stored password found; will not try to log in.");
None
}
} else {
info!("No stored username or default nick found; will not try to log in.");
None
}
}

View File

@ -19,6 +19,11 @@ use dioxus::hooks::use_state;
use dioxus::prelude::*; use dioxus::prelude::*;
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr;
use jid::BareJid;
use log::error;
use crate::passwords::Password;
use crate::types::LoginCredentials;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum LoginStatus { pub enum LoginStatus {
@ -49,6 +54,32 @@ impl Debug for LoginAttempt {
} }
} }
pub fn validate_login_attempt(x: LoginAttempt) -> Result<LoginCredentials, String> {
let LoginAttempt {
username,
default_nick,
password,
} = x;
// First, validate the username as a jid:
let jid = match BareJid::from_str(&username) {
Ok(x) => x,
Err(e) => {
error!("Invalid JID: {}", e);
return Err(format!("Invalid JID: {}", e));
}
};
// Wrap the password in a Password struct.
let password = Password(password);
Ok(LoginCredentials {
username: jid,
default_nick,
password,
})
}
/// The props for the login screen widget, including: /// The props for the login screen widget, including:
/// * The xmpp address to pre-fill in the username field. /// * The xmpp address to pre-fill in the username field.
/// * The default nick to pre-fill in the default nick field. /// * The default nick to pre-fill in the default nick field.

View File

@ -23,6 +23,7 @@ use crate::widgets::room_view::RoomView;
use crate::widgets::sidebar::SideBar; use crate::widgets::sidebar::SideBar;
use crate::xmpp_interface::NetworkCommand; use crate::xmpp_interface::NetworkCommand;
use dioxus::core::{Element, Scope}; use dioxus::core::{Element, Scope};
use crate::widgets::login_screen::validate_login_attempt;
use dioxus::prelude::*; use dioxus::prelude::*;
use futures_util::StreamExt; use futures_util::StreamExt;
@ -30,7 +31,7 @@ use jid::BareJid;
use log::{error, info}; use log::{error, info};
use std::collections::HashMap; use std::collections::HashMap;
use crate::configuration::{load_config, Configuration}; use crate::configuration::{load_config, Configuration, try_retrieve_credentials};
use crate::xmpp_interface; use crate::xmpp_interface;
use crate::configuration::store_login_details; use crate::configuration::store_login_details;
@ -46,24 +47,6 @@ pub mod room_view;
pub mod send_message; pub mod send_message;
pub mod sidebar; pub mod sidebar;
fn try_retrieve_credentials(config: &Configuration) -> Option<LoginCredentials> {
if let (Some(user), Some(nick)) = (&config.stored_username, &config.stored_default_nick) {
if let Some(password) = try_retrieve_password_from_keyring(&user) {
Some(LoginCredentials {
username: user.clone(),
default_nick: nick.clone(),
password,
})
} else {
info!("No stored password found; will not try to log in.");
None
}
} else {
info!("No stored username or default nick found; will not try to log in.");
None
}
}
pub fn App(cx: Scope) -> Element { pub fn App(cx: Scope) -> Element {
let messages = use_ref(cx, || HashMap::new()); let messages = use_ref(cx, || HashMap::new());
let current_room = use_state(cx, || None::<BareJid>); let current_room = use_state(cx, || None::<BareJid>);
@ -103,34 +86,18 @@ pub fn App(cx: Scope) -> Element {
cached_nick: config.read().stored_default_nick.clone().unwrap_or("".to_string()), cached_nick: config.read().stored_default_nick.clone().unwrap_or("".to_string()),
login_state: connection_status.current().as_ref().clone(), login_state: connection_status.current().as_ref().clone(),
on_login_attempt: move |x: LoginAttempt| { on_login_attempt: move |x: LoginAttempt| {
// Validate the login attempt.
let LoginAttempt { username, default_nick, password } = x; match validate_login_attempt(x) {
Ok(x) => {
// First, validate the username as a jid: config.with_mut(|c| {
let jid = match BareJid::from_str(&username) { store_login_details(x.username.clone(), x.default_nick.clone(), &x.password, c);
Ok(x) => x, });
Err(e) => { coroutine.send(NetworkCommand::TryLogin { credentials: x });
error!("Invalid JID: {}", e);
return;
}
};
// Wrap the password in a Password struct.
let password = Password(password);
// Store the login details in the config and keyring for auto-login next time.
config.with_mut(|c| {
store_login_details(jid.clone(), default_nick.clone(), &password, c);
});
// Finally, send the login command to the xmpp interface.
coroutine.send(NetworkCommand::TryLogin {
credentials: LoginCredentials {
username: jid,
default_nick,
password,
}, },
}); Err(e) => {
error!("Invalid login attempt: {}", e);
}
}
}, },
} }
} }
@ -138,7 +105,6 @@ pub fn App(cx: Scope) -> Element {
} else { } else {
// We're logged in; show a sidebar and a room view. // We're logged in; show a sidebar and a room view.
rsx!{ rsx!{
// Sidebar. // Sidebar.