diff --git a/paket/src/keyring.rs b/paket/src/keyring.rs new file mode 100644 index 0000000..d3f9132 --- /dev/null +++ b/paket/src/keyring.rs @@ -0,0 +1,59 @@ +use std::sync::OnceLock; + +pub static KEYRING: OnceLock = OnceLock::new(); + +fn get_keyring_base_attribute() -> (&'static str, &'static str) { + ("app", crate::constants::APP_ID) +} + +fn get_keyring_attributes_refresh_token() -> Vec<(&'static str, &'static str)> { + vec![get_keyring_base_attribute(), ("type", "refresh_token")] +} + +fn get_keyring_attributes_packstation() -> Vec<(&'static str, &'static str)> { + vec![ + get_keyring_base_attribute(), + ("type", "packstation-gerät-secret"), + ] +} + +pub async fn keyring_delete_all_items() -> oo7::Result<()> { + get_keyring() + .delete(&vec![get_keyring_base_attribute()]) + .await +} + +fn get_keyring<'a>() -> &'a oo7::Keyring { + KEYRING.get().unwrap() +} + +pub async fn keyring_get_refresh_token() -> oo7::Result> { + let items = get_keyring() + .search_items(&get_keyring_attributes_refresh_token()) + .await?; + + if let Some(item) = items.get(0) { + if item.is_locked().await? { + item.unlock().await?; + } + let data = item.secret().await.unwrap(); + Ok(Some(String::from_utf8(data.to_vec()).unwrap())) + } else { + Ok(None) + } +} + +pub async fn keyring_set_refresh_token(value: String) -> oo7::Result<()> { + get_keyring() + .create_item( + "Refresh Token", + &get_keyring_attributes_refresh_token(), + value, + true, + ) + .await +} + +pub fn keyring_is_available() -> bool { + KEYRING.get().is_some() +} diff --git a/paket/src/lib.rs b/paket/src/lib.rs index 058fda3..c116a5f 100644 --- a/paket/src/lib.rs +++ b/paket/src/lib.rs @@ -2,6 +2,7 @@ pub mod account; pub mod advice; pub mod advices; pub mod constants; +pub mod keyring; pub mod login; pub mod ready; pub mod tracking; diff --git a/paket/src/login.rs b/paket/src/login.rs index bba8b91..67400ce 100644 --- a/paket/src/login.rs +++ b/paket/src/login.rs @@ -1,9 +1,4 @@ -use std::{ - cell::RefCell, - collections::HashMap, - sync::{Arc, OnceLock}, - time::Duration, -}; +use std::{cell::RefCell, collections::HashMap, sync::Arc, time::Duration}; use adw::prelude::*; use libpaket::{ @@ -17,7 +12,7 @@ use relm4::{ }; use webkit::{prelude::WebViewExt, URIRequest, WebContext, WebView}; -static KEYRING: OnceLock = OnceLock::new(); +use crate::keyring::{keyring_get_refresh_token, keyring_is_available, keyring_set_refresh_token}; #[derive(Debug)] pub enum LoginInput { @@ -79,8 +74,19 @@ pub enum LoginCommand { NeedsRefresh, } -const KEYRING_ATTRIBUTES: [(&str, &str); 2] = - [("app", crate::constants::APP_ID), ("type", "refresh_token")]; +macro_rules! keyring_result_get { + ($sender: ident, $caller: expr, $code: expr) => {{ + let res = $caller; + + match res { + Ok(value) => Ok($code(value)), + Err(err) => { + $sender.output(LoginOutput::KeyringError(err)).unwrap(); + Err(()) + } + } + }}; +} #[relm4::component(async, pub)] impl AsyncComponent for Login { @@ -175,47 +181,27 @@ impl AsyncComponent for Login { tracker: 0, }; - let result = oo7::Keyring::new().await; - match result { - Ok(keyring) => { - KEYRING.set(keyring).unwrap(); - if let Err(err) = KEYRING.get().unwrap().unlock().await { - sender - .output(LoginOutput::KeyringError(err)) - .expect("sender not worky"); - } else { - let keyring = KEYRING.get().unwrap(); - match keyring - .search_items(&HashMap::from(KEYRING_ATTRIBUTES)) - .await - { - Ok(res) => { - if res.len() > 0 { - let item = &res[0]; - let refresh_token = item.secret().await.unwrap(); - let refresh_token = - std::str::from_utf8(refresh_token.as_slice()).unwrap(); - model.refresh_token = - Some(RefreshToken::new(refresh_token.to_string()).unwrap()); - sender.input(LoginInput::NeedsRefresh); - } else { - sender.input(LoginInput::NeedsLogin); - } - } - Err(err) => { - sender - .output(LoginOutput::KeyringError(err)) - .expect("sender not worky"); - } - }; - } + let _ = keyring_result_get!(sender, oo7::Keyring::new().await, |keyring| { + crate::keyring::KEYRING.set(keyring).unwrap(); + }); + + if keyring_is_available() { + let refresh_token = + keyring_result_get!(sender, keyring_get_refresh_token().await, move |value| { + return value; + }); + if let Ok(value) = refresh_token { + match value { + Some(value) => { + model.refresh_token = Some(RefreshToken::new(value).unwrap()); + sender.input(LoginInput::NeedsRefresh); + } + None => { + sender.input(LoginInput::NeedsLogin); + } + }; } - Err(err) => { - sender - .output(LoginOutput::KeyringError(err)) - .expect("sender not worky"); - } - }; + } let webcontext = WebContext::builder().build(); { @@ -273,7 +259,7 @@ impl AsyncComponent for Login { *token.write() = None; } if let Some(refresh_token) = self.refresh_token.clone() { - sender.command(|out, shutdown| { + sender.command(|_, shutdown| { shutdown .register(async move { let client = OpenIdClient::new(); @@ -283,10 +269,7 @@ impl AsyncComponent for Login { }); } self.refresh_token = None; - let keyring = KEYRING.get().unwrap(); - let _ = keyring - .delete(&HashMap::from([("app", crate::constants::APP_ID)])) - .await; + let _ = crate::keyring::keyring_delete_all_items().await; } LoginInput::NeedsRefresh => { let refresh_token = self.refresh_token.as_ref().unwrap().clone(); @@ -388,27 +371,21 @@ impl Login { .drop_on_shutdown() }); } - let future = async { - self.refresh_token = Some(res.refresh_token); - let keyring = KEYRING.get().unwrap(); - keyring - .create_item( - "Refresh Token", - &HashMap::from(KEYRING_ATTRIBUTES), - self.refresh_token.as_ref().unwrap().to_string(), - true, - ) - .await - .unwrap(); - }; + + self.refresh_token = Some(res.refresh_token); if !res.id_token.is_expired() { + let _ = keyring_result_get!( + sender, + keyring_set_refresh_token(self.refresh_token.as_ref().unwrap().to_string()) + .await, + |_| {} + ); let credentials_model = self.shared_id_token.lock().await; let mut credentials_model = credentials_model.write(); *credentials_model = Some(res.id_token); } - future.await; } Err(res) => { // We disarm the webkit flow/aka breaking the application. We want to reduce invalid requests