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
// 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 keyring::Entry;
use log::error;
use log::{error, info};
use serde_derive::{Deserialize, Serialize};
use crate::types::LoginCredentials;
/// The configuration struct containing all the configuration options.
#[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 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)]
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 xmpp address to pre-fill in the username 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::xmpp_interface::NetworkCommand;
use dioxus::core::{Element, Scope};
use crate::widgets::login_screen::validate_login_attempt;
use dioxus::prelude::*;
use futures_util::StreamExt;
@ -30,7 +31,7 @@ use jid::BareJid;
use log::{error, info};
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::configuration::store_login_details;
@ -46,24 +47,6 @@ pub mod room_view;
pub mod send_message;
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 {
let messages = use_ref(cx, || HashMap::new());
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()),
login_state: connection_status.current().as_ref().clone(),
on_login_attempt: move |x: LoginAttempt| {
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;
}
};
// 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,
// Validate the login attempt.
match validate_login_attempt(x) {
Ok(x) => {
config.with_mut(|c| {
store_login_details(x.username.clone(), x.default_nick.clone(), &x.password, c);
});
coroutine.send(NetworkCommand::TryLogin { credentials: x });
},
});
Err(e) => {
error!("Invalid login attempt: {}", e);
}
}
},
}
}
@ -138,7 +105,6 @@ pub fn App(cx: Scope) -> Element {
} else {
// We're logged in; show a sidebar and a room view.
rsx!{
// Sidebar.