fix permissions errors and terminal launching

This commit is contained in:
2024-06-29 20:42:51 -04:00
parent acc5c71bfa
commit f311fde74e
17 changed files with 620 additions and 86 deletions

View File

@ -43,6 +43,8 @@ pub fn run() -> tauri::Result<()> {
.error_popup("Failed to show main window")
}))
.plugin(tauri_plugin_global_shortcut::Builder::default().build())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![
ipc::unlock,
ipc::lock,
@ -60,17 +62,7 @@ pub fn run() -> tauri::Result<()> {
ipc::get_setup_errors,
ipc::exit,
])
.setup(|app| {
let res = rt::block_on(setup(app));
if let Err(ref e) = res {
MessageDialog::new()
.set_level(MessageLevel::Error)
.set_title("Creddy failed to start")
.set_description(format!("{e}"))
.show();
}
res
})
.setup(|app| rt::block_on(setup(app)))
.build(tauri::generate_context!())?
.run(|app, run_event| {
if let RunEvent::WindowEvent { event, .. } = run_event {

View File

@ -38,10 +38,10 @@ struct CredentialRow {
pub struct CredentialRecord {
#[serde(serialize_with = "serialize_uuid")]
#[serde(deserialize_with = "deserialize_uuid")]
id: Uuid, // UUID so it can be generated on the frontend
name: String, // user-facing identifier so it can be changed
is_default: bool,
credential: Credential,
pub id: Uuid, // UUID so it can be generated on the frontend
pub name: String, // user-facing identifier so it can be changed
pub is_default: bool,
pub credential: Credential,
}
impl CredentialRecord {

View File

@ -152,7 +152,8 @@ pub async fn save_config(config: AppConfig, app_state: State<'_, AppState>) -> R
#[tauri::command]
pub async fn launch_terminal(base: bool) -> Result<(), LaunchTerminalError> {
terminal::launch(base).await
let res = terminal::launch(base).await;
res
}

View File

@ -145,11 +145,11 @@ async fn get_aws_credentials(
match response.approval {
Approval::Approved => {
if response.base {
let creds = state.get_aws_base("default").await?;
let creds = state.get_aws_default().await?;
Ok(Response::AwsBase(creds))
}
else {
let creds = state.get_aws_session("default").await?;
let creds = state.get_aws_default_session().await?;
Ok(Response::AwsSession(creds.clone()))
}
},

View File

@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::time::Duration;
use time::OffsetDateTime;
@ -21,6 +22,7 @@ use crate::credentials::{
use crate::{config, config::AppConfig};
use crate::credentials::{
AwsBaseCredential,
Credential,
CredentialRecord,
PersistentCredential
};
@ -107,7 +109,8 @@ impl VisibilityLease {
pub struct AppState {
pub config: RwLock<AppConfig>,
pub app_session: RwLock<AppSession>,
pub aws_session: RwLock<Option<AwsSessionCredential>>,
// session cache is keyed on id rather than name because names can change
pub aws_sessions: RwLock<HashMap<Uuid, AwsSessionCredential>>,
pub last_activity: RwLock<OffsetDateTime>,
pub request_count: RwLock<u64>,
pub waiting_requests: RwLock<HashMap<u64, Sender<RequestResponse>>>,
@ -130,7 +133,7 @@ impl AppState {
AppState {
config: RwLock::new(config),
app_session: RwLock::new(app_session),
aws_session: RwLock::new(None),
aws_sessions: RwLock::new(HashMap::new()),
last_activity: RwLock::new(OffsetDateTime::now_utc()),
request_count: RwLock::new(0),
waiting_requests: RwLock::new(HashMap::new()),
@ -261,26 +264,41 @@ impl AppState {
Ok(())
}
pub async fn get_aws_base(&self, name: &str) -> Result<AwsBaseCredential, GetCredentialsError> {
pub async fn get_aws_default(&self) -> Result<AwsBaseCredential, GetCredentialsError> {
let app_session = self.app_session.read().await;
let crypto = app_session.try_get_crypto()?;
let creds = AwsBaseCredential::load_by_name(name, crypto, &self.pool).await?;
let record = CredentialRecord::load_default("aws", crypto, &self.pool).await?;
let creds = match record.credential {
Credential::AwsBase(b) => Ok(b),
_ => Err(LoadCredentialsError::NoCredentials)
}?;
Ok(creds)
}
pub async fn get_aws_session(&self, name: &str) -> Result<RwLockReadGuard<'_, AwsSessionCredential>, GetCredentialsError> {
// yes, this sometimes results in double-fetching base credentials from disk
// I'm done trying to be optimal
pub async fn get_aws_default_session(&self) -> Result<RwLockReadGuard<'_, AwsSessionCredential>, GetCredentialsError> {
let app_session = self.app_session.read().await;
let crypto = app_session.try_get_crypto()?;
let record = CredentialRecord::load_default("aws", crypto, &self.pool).await?;
let base = match &record.credential {
Credential::AwsBase(b) => Ok(b),
_ => Err(LoadCredentialsError::NoCredentials)
}?;
{
let mut aws_session = self.aws_session.write().await;
if aws_session.is_none() || aws_session.as_ref().unwrap().is_expired() {
let base_creds = self.get_aws_base(name).await?;
*aws_session = Some(AwsSessionCredential::from_base(&base_creds).await?);
let mut aws_sessions = self.aws_sessions.write().await;
match aws_sessions.entry(record.id) {
Entry::Vacant(e) => {
e.insert(AwsSessionCredential::from_base(&base).await?);
},
Entry::Occupied(mut e) if e.get().is_expired() => {
*(e.get_mut()) = AwsSessionCredential::from_base(&base).await?;
},
_ => ()
}
}
// we know this is safe, because we juse made sure of it
let s = RwLockReadGuard::map(self.aws_session.read().await, |opt| opt.as_ref().unwrap());
// we know the unwrap is safe, because we just made sure of it
let s = RwLockReadGuard::map(self.aws_sessions.read().await, |map| map.get(&record.id).unwrap());
Ok(s)
}

View File

@ -1,7 +1,7 @@
use std::process::Command;
use std::time::Duration;
use tauri::Manager;
use tauri::{AppHandle, Manager};
use tokio::time::sleep;
use crate::app::APP;
@ -18,6 +18,18 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
return Ok(());
}
let res = do_launch(app, use_base).await;
state.unregister_terminal_request().await;
res
}
// this handles most of the work, the outer function is just to ensure we properly
// unregister the request if there's an error
async fn do_launch(app: &AppHandle, use_base: bool) -> Result<(), LaunchTerminalError> {
let state = app.state::<AppState>();
let mut cmd = {
let config = state.config.read().await;
let mut cmd = Command::new(&config.terminal.exec);
@ -41,7 +53,6 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
_ = rx => lease.release(),
// otherwise, dump this request, but return Ok so we don't get an error popup
_ = sleep(timeout) => {
state.unregister_terminal_request().await;
eprintln!("WARNING: Request to launch terminal timed out after 60 seconds.");
return Ok(());
},
@ -52,27 +63,24 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
// (i.e. lies about unlocking) we could end up here with a locked session
// this will result in an error popup to the user (see main hotkey handler)
if use_base {
let base_creds = state.get_aws_base("default").await?;
let base_creds = state.get_aws_default().await?;
cmd.env("AWS_ACCESS_KEY_ID", &base_creds.access_key_id);
cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key);
}
else {
let session_creds = state.get_aws_session("default").await?;
let session_creds = state.get_aws_default_session().await?;
cmd.env("AWS_ACCESS_KEY_ID", &session_creds.access_key_id);
cmd.env("AWS_SECRET_ACCESS_KEY", &session_creds.secret_access_key);
cmd.env("AWS_SESSION_TOKEN", &session_creds.session_token);
}
let res = match cmd.spawn() {
match cmd.spawn() {
Ok(_) => Ok(()),
Err(e) if std::io::ErrorKind::NotFound == e.kind() => {
Err(ExecError::NotFound(cmd.get_program().to_owned()))
},
Err(e) => Err(ExecError::ExecutionFailed(e)),
};
}?;
state.unregister_terminal_request().await;
res?; // ? auto-conversion is more liberal than .into()
Ok(())
}