Browse Source

secret: Implement more methods on StoredSession

merge-requests/1327/merge
Kévin Commaille 3 years ago
parent
commit
fb1dba1a00
No known key found for this signature in database
GPG Key ID: 29A48C1F03620416
  1. 6
      src/login/mod.rs
  2. 137
      src/secret.rs
  3. 27
      src/session/mod.rs
  4. 2
      src/window.rs

6
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}");

137
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<Item>, 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<Vec<StoredSession>, 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(())
}

27
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<Self, ClientSetupError> {
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();

2
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
}

Loading…
Cancel
Save