From fb1dba1a002c4e34b2989ef84379a604abd09ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Wed, 17 May 2023 16:12:34 +0200 Subject: [PATCH] secret: Implement more methods on StoredSession --- src/login/mod.rs | 6 +- src/secret.rs | 137 +++++++++++++++++++++++++-------------------- src/session/mod.rs | 27 ++------- src/window.rs | 2 +- 4 files changed, 83 insertions(+), 89 deletions(-) diff --git a/src/login/mod.rs b/src/login/mod.rs index 15b15e5f..cb1af105 100644 --- a/src/login/mod.rs +++ b/src/login/mod.rs @@ -21,8 +21,8 @@ use self::{ method_page::LoginMethodPage, sso_page::LoginSsoPage, }; use crate::{ - secret, session::SessionVerification, spawn, spawn_tokio, toast, - user_facing_error::UserFacingError, Application, Session, Window, RUNTIME, + session::SessionVerification, spawn, spawn_tokio, toast, user_facing_error::UserFacingError, + Application, Session, Window, RUNTIME, }; #[derive(Clone, Debug, glib::Boxed)] @@ -512,7 +512,7 @@ impl Login { } let session_info = session.info().clone(); - let handle = spawn_tokio!(async move { secret::store_session(&session_info).await }); + let handle = spawn_tokio!(async move { session_info.store().await }); if let Err(error) = handle.await.unwrap() { error!("Couldn't store session: {error}"); diff --git a/src/secret.rs b/src/secret.rs index fad43c4c..a5267f0c 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -1,9 +1,11 @@ use std::{collections::HashMap, ffi::OsStr, fmt, fs, path::PathBuf, string::FromUtf8Error}; use gettextrs::gettext; +use gtk::glib; use log::{debug, error, warn}; use matrix_sdk::ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId}; use oo7::{Item, Keyring}; +use rand::{distributions::Alphanumeric, thread_rng, Rng}; use serde::{Deserialize, Serialize}; use serde_json::error::Error as JsonError; use thiserror::Error; @@ -306,13 +308,8 @@ impl StoredSession { } } - /// Construct a `StoredSession` from its parts. - pub fn from_parts( - homeserver: Url, - path: PathBuf, - passphrase: String, - data: matrix_sdk::Session, - ) -> Self { + /// Construct a `StoredSession` from the given login data. + pub fn with_login_data(homeserver: Url, data: matrix_sdk::Session) -> Self { let matrix_sdk::Session { access_token, user_id, @@ -320,6 +317,15 @@ impl StoredSession { .. } = data; + let mut path = glib::user_data_dir(); + path.push(glib::uuid_string_random().as_str()); + + let passphrase = thread_rng() + .sample_iter(Alphanumeric) + .take(30) + .map(char::from) + .collect(); + let secret = Secret { access_token, passphrase, @@ -383,48 +389,93 @@ impl StoredSession { .unwrap() } + /// Write this session to the `SecretService`, overwriting any previously + /// stored session with the same attributes. + pub async fn store(&self) -> Result<(), SecretError> { + let keyring = Keyring::new().await?; + + let attrs = self.attributes(); + let attributes = attrs.iter().map(|(k, v)| (*k, v.as_ref())).collect(); + let secret = rmp_serde::to_vec_named(&self.secret).unwrap(); + + keyring + .create_item( + &gettext_f( + // Translators: Do NOT translate the content between '{' and '}', this is a + // variable name. + "Fractal: Matrix credentials for {user_id}", + &[("user_id", self.user_id.as_str())], + ), + attributes, + secret, + true, + ) + .await?; + + Ok(()) + } + /// Delete this session from the system. - pub async fn delete(self, item: Item) { + pub async fn delete(self, item: Option, logout: bool) { debug!( - "Removing session {} found for user {} with ID {}…", + "Removing stored session {} with version {} for Matrix user {}…", + self.id(), self.version, self.user_id, - self.id() ); spawn_tokio!(async move { - match matrix::client_with_stored_session(self.clone()).await { - Ok(client) => { - if let Err(error) = client.logout().await { - error!("Failed to log out old session: {error}"); + if logout { + debug!("Logging out session"); + match matrix::client_with_stored_session(self.clone()).await { + Ok(client) => { + if let Err(error) = client.logout().await { + error!("Failed to log out session: {error}"); + } + } + Err(error) => { + error!("Failed to build client to log out session: {error}") } - } - Err(error) => { - error!("Failed to build client to log out old session: {error}") } } - if let Err(error) = item.delete().await { - error!("Failed to delete old session from Secret Service: {error}"); - }; + if let Some(item) = item { + if let Err(error) = item.delete().await { + error!("Failed to delete session item from Secret Service: {error}"); + }; + } else if let Err(error) = self.delete_from_secret_service().await { + error!("Failed to delete session data from Secret Service: {error}"); + } if let Err(error) = fs::remove_dir_all(self.path) { - error!("Failed to remove database from old session: {error}"); + error!("Failed to remove session database: {error}"); } }) .await .unwrap(); } + /// Remove this session from the `SecretService` + async fn delete_from_secret_service(&self) -> Result<(), SecretError> { + let keyring = Keyring::new().await?; + + let attrs = self.attributes(); + let attributes = attrs.iter().map(|(k, v)| (*k, v.as_ref())).collect(); + + keyring.delete(attributes).await?; + + Ok(()) + } + /// Migrate this session to version 2. /// /// This implies moving the database under the profile's directory. pub async fn migrate_to_v2(mut self, item: Item) { warn!( - "Session version {} found for user {} with ID {}, migrating to version 2…", + "Session {} with version {} found for user {}, migrating to version 2…", + self.id(), self.version, self.user_id, - self.id() ); spawn_tokio!(async move { @@ -448,7 +499,7 @@ impl StoredSession { error!("Failed to remove outdated session: {error}"); } - if let Err(error) = store_session(&self).await { + if let Err(error) = self.store().await { error!("Failed to store updated session: {error}"); } }) @@ -511,41 +562,3 @@ pub async fn restore_sessions() -> Result, SecretError> { Ok(sessions) } - -/// Writes a session to the `SecretService`, overwriting any previously stored -/// session with the same `homeserver`, `username` and `device-id`. -pub async fn store_session(session: &StoredSession) -> Result<(), SecretError> { - let keyring = Keyring::new().await?; - - let attrs = session.attributes(); - let attributes = attrs.iter().map(|(k, v)| (*k, v.as_ref())).collect(); - let secret = rmp_serde::to_vec_named(&session.secret).unwrap(); - - keyring - .create_item( - &gettext_f( - // Translators: Do NOT translate the content between '{' and '}', this is a - // variable name. - "Fractal: Matrix credentials for {user_id}", - &[("user_id", session.user_id.as_str())], - ), - attributes, - secret, - true, - ) - .await?; - - Ok(()) -} - -/// Removes a session from the `SecretService` -pub async fn remove_session(session: &StoredSession) -> Result<(), SecretError> { - let keyring = Keyring::new().await?; - - let attrs = session.attributes(); - let attributes = attrs.iter().map(|(k, v)| (*k, v.as_ref())).collect(); - - keyring.delete(attributes).await?; - - Ok(()) -} diff --git a/src/session/mod.rs b/src/session/mod.rs index f376f42f..6bd21d5b 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -14,7 +14,7 @@ mod sidebar; mod user; pub mod verification; -use std::{collections::HashSet, fs, time::Duration}; +use std::{collections::HashSet, time::Duration}; use adw::{prelude::*, subclass::prelude::*}; use futures::StreamExt; @@ -44,7 +44,6 @@ use matrix_sdk::{ sync::SyncResponse, Client, }; -use rand::{distributions::Alphanumeric, thread_rng, Rng}; use tokio::task::JoinHandle; use url::Url; @@ -63,7 +62,7 @@ pub use self::{ user::{User, UserActions, UserExt}, }; use crate::{ - secret::{self, StoredSession}, + secret::StoredSession, session::sidebar::ItemList, spawn, spawn_tokio, toast, utils::{ @@ -346,16 +345,7 @@ glib::wrapper! { impl Session { /// Create a new session. pub async fn new(homeserver: Url, data: matrix_sdk::Session) -> Result { - let mut path = glib::user_data_dir(); - path.push(glib::uuid_string_random().as_str()); - - let passphrase = thread_rng() - .sample_iter(Alphanumeric) - .take(30) - .map(char::from) - .collect(); - - let stored_session = StoredSession::from_parts(homeserver, path, passphrase, data); + let stored_session = StoredSession::with_login_data(homeserver, data); Self::restore(stored_session).await } @@ -743,7 +733,6 @@ impl Session { async fn cleanup_session(&self) { let imp = self.imp(); - let info = imp.info.get().unwrap(); self.set_state(SessionState::LoggedOut); @@ -755,15 +744,7 @@ impl Session { settings.delete(); } - let session_info = info.clone(); - let handle = spawn_tokio!(async move { secret::remove_session(&session_info).await }); - if let Err(error) = handle.await.unwrap() { - error!("Failed to remove credentials from SecretService after logout: {error}"); - } - - if let Err(error) = fs::remove_dir_all(info.path.clone()) { - error!("Failed to remove database after logout: {error}"); - } + imp.info.get().unwrap().clone().delete(None, false).await; self.notifications().clear(); diff --git a/src/window.rs b/src/window.rs index 97b5ed41..94399dfd 100644 --- a/src/window.rs +++ b/src/window.rs @@ -274,7 +274,7 @@ impl Window { SecretError::OldVersion { item, session } => { if session.version == 0 { warn!("Found old session with sled store, removing…"); - session.delete(item).await + session.delete(Some(item), true).await } else if session.version < 2 { session.migrate_to_v2(item).await }