fix permissions errors and terminal launching

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

151
src-tauri/Cargo.lock generated
View File

@ -134,14 +134,13 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093" checksum = "dd884d7c72877a94102c3715f3b1cd09ff4fac28221add3e57cfbe25c236d093"
dependencies = [ dependencies = [
"async-fs",
"async-net",
"enumflags2", "enumflags2",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"rand 0.8.5", "rand 0.8.5",
"serde", "serde",
"serde_repr", "serde_repr",
"tokio",
"url", "url",
"zbus", "zbus",
] ]
@ -224,17 +223,6 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "async-net"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
dependencies = [
"async-io",
"blocking",
"futures-lite",
]
[[package]] [[package]]
name = "async-process" name = "async-process"
version = "2.2.3" version = "2.2.3"
@ -942,9 +930,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.100" version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
[[package]] [[package]]
name = "cesu8" name = "cesu8"
@ -1221,7 +1209,7 @@ dependencies = [
"dirs 5.0.1", "dirs 5.0.1",
"is-terminal", "is-terminal",
"once_cell", "once_cell",
"rfd", "rfd 0.13.0",
"serde", "serde",
"serde_json", "serde_json",
"signature 2.2.0", "signature 2.2.0",
@ -1234,7 +1222,9 @@ dependencies = [
"sysinfo", "sysinfo",
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-global-shortcut", "tauri-plugin-global-shortcut",
"tauri-plugin-os",
"tauri-plugin-single-instance", "tauri-plugin-single-instance",
"thiserror", "thiserror",
"time", "time",
@ -1653,9 +1643,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.12.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@ -2139,6 +2129,16 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.1.16" version = "0.1.16"
@ -2957,9 +2957,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.21" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "loom" name = "loom"
@ -3445,6 +3445,17 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "os_info"
version = "3.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
dependencies = [
"log",
"serde",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.6.1" version = "6.6.1"
@ -3842,12 +3853,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "pollster"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
[[package]] [[package]]
name = "poly1305" name = "poly1305"
version = "0.8.0" version = "0.8.0"
@ -4223,6 +4228,29 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "rfd"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0d8ab342bcc5436e04d3a4c1e09e17d74958bfaddf8d5fad6f85607df0f994f"
dependencies = [
"block",
"dispatch",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys",
"log",
"objc",
"objc-foundation",
"objc_id",
"raw-window-handle 0.5.2",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "rfd" name = "rfd"
version = "0.14.1" version = "0.14.1"
@ -4232,14 +4260,15 @@ dependencies = [
"ashpd", "ashpd",
"block", "block",
"dispatch", "dispatch",
"glib-sys",
"gobject-sys",
"gtk-sys",
"js-sys", "js-sys",
"log", "log",
"objc", "objc",
"objc-foundation", "objc-foundation",
"objc_id", "objc_id",
"pollster",
"raw-window-handle 0.6.2", "raw-window-handle 0.6.2",
"urlencoding",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
@ -5225,6 +5254,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "sys-locale"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.26.9" version = "0.26.9"
@ -5438,6 +5476,43 @@ dependencies = [
"walkdir", "walkdir",
] ]
[[package]]
name = "tauri-plugin-dialog"
version = "2.0.0-beta.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed4b22c59f7b04ae2a0bed8241aa715b41973c3f042c84aa67a1f4dc0174a8d"
dependencies = [
"dunce",
"log",
"raw-window-handle 0.6.2",
"rfd 0.14.1",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror",
]
[[package]]
name = "tauri-plugin-fs"
version = "2.0.0-beta.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3aa91955751f329e0aa431b87c199b7378b6f91ec0765d2ad9d4c64e017c3cda"
dependencies = [
"anyhow",
"glob",
"schemars",
"serde",
"serde_json",
"serde_repr",
"tauri",
"tauri-plugin",
"thiserror",
"url",
"uuid",
]
[[package]] [[package]]
name = "tauri-plugin-global-shortcut" name = "tauri-plugin-global-shortcut"
version = "2.0.0-beta.6" version = "2.0.0-beta.6"
@ -5453,6 +5528,24 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "tauri-plugin-os"
version = "2.0.0-beta.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ae3c8aeb113ce614cc36a43b1061fdf64381f7635d02c390052ce7579ec628"
dependencies = [
"gethostname",
"log",
"os_info",
"serde",
"serde_json",
"serialize-to-javascript",
"sys-locale",
"tauri",
"tauri-plugin",
"thiserror",
]
[[package]] [[package]]
name = "tauri-plugin-single-instance" name = "tauri-plugin-single-instance"
version = "2.0.0-beta.9" version = "2.0.0-beta.9"
@ -5691,6 +5784,7 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"tracing",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -6879,6 +6973,7 @@ dependencies = [
"serde_repr", "serde_repr",
"sha1", "sha1",
"static_assertions", "static_assertions",
"tokio",
"tracing", "tracing",
"uds_windows", "uds_windows",
"windows-sys 0.52.0", "windows-sys 0.52.0",

View File

@ -48,7 +48,9 @@ windows = { version = "0.51.1", features = ["Win32_Foundation", "Win32_System_Pi
time = "0.3.31" time = "0.3.31"
tauri-plugin-single-instance = "2.0.0-beta.9" tauri-plugin-single-instance = "2.0.0-beta.9"
tauri-plugin-global-shortcut = "2.0.0-beta.6" tauri-plugin-global-shortcut = "2.0.0-beta.6"
rfd = "0.14.1" tauri-plugin-os = "2.0.0-beta.6"
tauri-plugin-dialog = "2.0.0-beta.9"
rfd = "0.13.0"
ssh-agent-lib = "0.4.0" ssh-agent-lib = "0.4.0"
ssh-key = { version = "0.6.6", features = ["rsa", "ed25519", "encryption"] } ssh-key = { version = "0.6.6", features = ["rsa", "ed25519", "encryption"] }
signature = "2.2.0" signature = "2.2.0"

View File

@ -12,6 +12,8 @@
"app:default", "app:default",
"resources:default", "resources:default",
"menu:default", "menu:default",
"tray:default" "tray:default",
"os:allow-os-type",
"dialog:allow-open"
] ]
} }

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default"]}} {"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","os:allow-os-type","dialog:allow-open"]}}

View File

@ -247,6 +247,82 @@
"app:deny-version" "app:deny-version"
] ]
}, },
{
"type": "string",
"enum": [
"dialog:default"
]
},
{
"description": "dialog:allow-ask -> Enables the ask command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-ask"
]
},
{
"description": "dialog:allow-confirm -> Enables the confirm command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-confirm"
]
},
{
"description": "dialog:allow-message -> Enables the message command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-message"
]
},
{
"description": "dialog:allow-open -> Enables the open command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-open"
]
},
{
"description": "dialog:allow-save -> Enables the save command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-save"
]
},
{
"description": "dialog:deny-ask -> Denies the ask command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-ask"
]
},
{
"description": "dialog:deny-confirm -> Denies the confirm command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-confirm"
]
},
{
"description": "dialog:deny-message -> Denies the message command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-message"
]
},
{
"description": "dialog:deny-open -> Denies the open command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-open"
]
},
{
"description": "dialog:deny-save -> Denies the save command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-save"
]
},
{ {
"description": "event:default -> Default permissions for the plugin.", "description": "event:default -> Default permissions for the plugin.",
"type": "string", "type": "string",
@ -778,6 +854,124 @@
"menu:deny-text" "menu:deny-text"
] ]
}, },
{
"type": "string",
"enum": [
"os:default"
]
},
{
"description": "os:allow-arch -> Enables the arch command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-arch"
]
},
{
"description": "os:allow-exe-extension -> Enables the exe_extension command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-exe-extension"
]
},
{
"description": "os:allow-family -> Enables the family command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-family"
]
},
{
"description": "os:allow-hostname -> Enables the hostname command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-hostname"
]
},
{
"description": "os:allow-locale -> Enables the locale command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-locale"
]
},
{
"description": "os:allow-os-type -> Enables the os_type command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-os-type"
]
},
{
"description": "os:allow-platform -> Enables the platform command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-platform"
]
},
{
"description": "os:allow-version -> Enables the version command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-version"
]
},
{
"description": "os:deny-arch -> Denies the arch command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-arch"
]
},
{
"description": "os:deny-exe-extension -> Denies the exe_extension command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-exe-extension"
]
},
{
"description": "os:deny-family -> Denies the family command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-family"
]
},
{
"description": "os:deny-hostname -> Denies the hostname command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-hostname"
]
},
{
"description": "os:deny-locale -> Denies the locale command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-locale"
]
},
{
"description": "os:deny-os-type -> Denies the os_type command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-os-type"
]
},
{
"description": "os:deny-platform -> Denies the platform command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-platform"
]
},
{
"description": "os:deny-version -> Denies the version command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-version"
]
},
{ {
"description": "path:default -> Default permissions for the plugin.", "description": "path:default -> Default permissions for the plugin.",
"type": "string", "type": "string",

View File

@ -247,6 +247,82 @@
"app:deny-version" "app:deny-version"
] ]
}, },
{
"type": "string",
"enum": [
"dialog:default"
]
},
{
"description": "dialog:allow-ask -> Enables the ask command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-ask"
]
},
{
"description": "dialog:allow-confirm -> Enables the confirm command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-confirm"
]
},
{
"description": "dialog:allow-message -> Enables the message command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-message"
]
},
{
"description": "dialog:allow-open -> Enables the open command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-open"
]
},
{
"description": "dialog:allow-save -> Enables the save command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:allow-save"
]
},
{
"description": "dialog:deny-ask -> Denies the ask command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-ask"
]
},
{
"description": "dialog:deny-confirm -> Denies the confirm command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-confirm"
]
},
{
"description": "dialog:deny-message -> Denies the message command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-message"
]
},
{
"description": "dialog:deny-open -> Denies the open command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-open"
]
},
{
"description": "dialog:deny-save -> Denies the save command without any pre-configured scope.",
"type": "string",
"enum": [
"dialog:deny-save"
]
},
{ {
"description": "event:default -> Default permissions for the plugin.", "description": "event:default -> Default permissions for the plugin.",
"type": "string", "type": "string",
@ -778,6 +854,124 @@
"menu:deny-text" "menu:deny-text"
] ]
}, },
{
"type": "string",
"enum": [
"os:default"
]
},
{
"description": "os:allow-arch -> Enables the arch command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-arch"
]
},
{
"description": "os:allow-exe-extension -> Enables the exe_extension command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-exe-extension"
]
},
{
"description": "os:allow-family -> Enables the family command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-family"
]
},
{
"description": "os:allow-hostname -> Enables the hostname command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-hostname"
]
},
{
"description": "os:allow-locale -> Enables the locale command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-locale"
]
},
{
"description": "os:allow-os-type -> Enables the os_type command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-os-type"
]
},
{
"description": "os:allow-platform -> Enables the platform command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-platform"
]
},
{
"description": "os:allow-version -> Enables the version command without any pre-configured scope.",
"type": "string",
"enum": [
"os:allow-version"
]
},
{
"description": "os:deny-arch -> Denies the arch command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-arch"
]
},
{
"description": "os:deny-exe-extension -> Denies the exe_extension command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-exe-extension"
]
},
{
"description": "os:deny-family -> Denies the family command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-family"
]
},
{
"description": "os:deny-hostname -> Denies the hostname command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-hostname"
]
},
{
"description": "os:deny-locale -> Denies the locale command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-locale"
]
},
{
"description": "os:deny-os-type -> Denies the os_type command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-os-type"
]
},
{
"description": "os:deny-platform -> Denies the platform command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-platform"
]
},
{
"description": "os:deny-version -> Denies the version command without any pre-configured scope.",
"type": "string",
"enum": [
"os:deny-version"
]
},
{ {
"description": "path:default -> Default permissions for the plugin.", "description": "path:default -> Default permissions for the plugin.",
"type": "string", "type": "string",

View File

@ -43,6 +43,8 @@ pub fn run() -> tauri::Result<()> {
.error_popup("Failed to show main window") .error_popup("Failed to show main window")
})) }))
.plugin(tauri_plugin_global_shortcut::Builder::default().build()) .plugin(tauri_plugin_global_shortcut::Builder::default().build())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
ipc::unlock, ipc::unlock,
ipc::lock, ipc::lock,
@ -60,17 +62,7 @@ pub fn run() -> tauri::Result<()> {
ipc::get_setup_errors, ipc::get_setup_errors,
ipc::exit, ipc::exit,
]) ])
.setup(|app| { .setup(|app| rt::block_on(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
})
.build(tauri::generate_context!())? .build(tauri::generate_context!())?
.run(|app, run_event| { .run(|app, run_event| {
if let RunEvent::WindowEvent { event, .. } = run_event { if let RunEvent::WindowEvent { event, .. } = run_event {

View File

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

View File

@ -152,7 +152,8 @@ pub async fn save_config(config: AppConfig, app_state: State<'_, AppState>) -> R
#[tauri::command] #[tauri::command]
pub async fn launch_terminal(base: bool) -> Result<(), LaunchTerminalError> { 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 { match response.approval {
Approval::Approved => { Approval::Approved => {
if response.base { if response.base {
let creds = state.get_aws_base("default").await?; let creds = state.get_aws_default().await?;
Ok(Response::AwsBase(creds)) Ok(Response::AwsBase(creds))
} }
else { else {
let creds = state.get_aws_session("default").await?; let creds = state.get_aws_default_session().await?;
Ok(Response::AwsSession(creds.clone())) Ok(Response::AwsSession(creds.clone()))
} }
}, },

View File

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

View File

@ -1,7 +1,7 @@
use std::process::Command; use std::process::Command;
use std::time::Duration; use std::time::Duration;
use tauri::Manager; use tauri::{AppHandle, Manager};
use tokio::time::sleep; use tokio::time::sleep;
use crate::app::APP; use crate::app::APP;
@ -18,6 +18,18 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
return Ok(()); 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 mut cmd = {
let config = state.config.read().await; let config = state.config.read().await;
let mut cmd = Command::new(&config.terminal.exec); let mut cmd = Command::new(&config.terminal.exec);
@ -41,7 +53,6 @@ pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
_ = rx => lease.release(), _ = rx => lease.release(),
// otherwise, dump this request, but return Ok so we don't get an error popup // otherwise, dump this request, but return Ok so we don't get an error popup
_ = sleep(timeout) => { _ = sleep(timeout) => {
state.unregister_terminal_request().await;
eprintln!("WARNING: Request to launch terminal timed out after 60 seconds."); eprintln!("WARNING: Request to launch terminal timed out after 60 seconds.");
return Ok(()); 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 // (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) // this will result in an error popup to the user (see main hotkey handler)
if use_base { 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_ACCESS_KEY_ID", &base_creds.access_key_id);
cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key); cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key);
} }
else { 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_ACCESS_KEY_ID", &session_creds.access_key_id);
cmd.env("AWS_SECRET_ACCESS_KEY", &session_creds.secret_access_key); cmd.env("AWS_SECRET_ACCESS_KEY", &session_creds.secret_access_key);
cmd.env("AWS_SESSION_TOKEN", &session_creds.session_token); cmd.env("AWS_SESSION_TOKEN", &session_creds.session_token);
} }
let res = match cmd.spawn() { match cmd.spawn() {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) if std::io::ErrorKind::NotFound == e.kind() => { Err(e) if std::io::ErrorKind::NotFound == e.kind() => {
Err(ExecError::NotFound(cmd.get_program().to_owned())) Err(ExecError::NotFound(cmd.get_program().to_owned()))
}, },
Err(e) => Err(ExecError::ExecutionFailed(e)), Err(e) => Err(ExecError::ExecutionFailed(e)),
}; }?;
state.unregister_terminal_request().await;
res?; // ? auto-conversion is more liberal than .into()
Ok(()) Ok(())
} }

View File

@ -7,6 +7,13 @@
export let value; export let value;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
async function pickFile() {
let file = await open();
if (file) {
value = file.path
}
}
</script> </script>
@ -19,8 +26,9 @@
on:change={() => dispatch('update', {value})} on:change={() => dispatch('update', {value})}
> >
<button <button
type="button"
class="btn btn-sm btn-primary" class="btn btn-sm btn-primary"
on:click={async () => value = await open()} on:click={pickFile}
>Browse</button> >Browse</button>
</div> </div>
<slot name="description" slot="description"></slot> <slot name="description" slot="description"></slot>

View File

@ -8,11 +8,15 @@
import Icon from '../ui/Icon.svelte'; import Icon from '../ui/Icon.svelte';
import Link from '../ui/Link.svelte'; import Link from '../ui/Link.svelte';
let launchTerminalError;
let launchBase = false; async function launchTerminal() {
function launchTerminal() { try {
invoke('launch_terminal', {base: launchBase}); await invoke('launch_terminal', {base: false});
launchBase = false; }
catch (e) {
console.log(e);
launchTerminalError = e;
}
} }
async function lock() { async function lock() {
@ -49,16 +53,16 @@
</Link> </Link>
<Link target={lock}> <Link target={lock}>
<div class="flex flex-col items-center gap-4 h-full max-w-56 rounded-box p-4 border border-accent hover:bg-base-200 transition-colors"> <div class="flex flex-col items-center gap-4 h-full max-w-56 rounded-box p-4 border border-warning hover:bg-base-200 transition-colors">
<Icon name="shield-check" class="size-12 stroke-1 stroke-accent" /> <Icon name="shield-check" class="size-12 stroke-1 stroke-warning" />
<h3 class="text-lg font-bold">Lock</h3> <h3 class="text-lg font-bold">Lock</h3>
<p class="text-sm">Lock Creddy.</p> <p class="text-sm">Lock Creddy.</p>
</div> </div>
</Link> </Link>
<Link target={() => invoke('exit')}> <Link target={() => invoke('exit')}>
<div class="flex flex-col items-center gap-4 h-full max-w-56 rounded-box p-4 border border-warning hover:bg-base-200 transition-colors"> <div class="flex flex-col items-center gap-4 h-full max-w-56 rounded-box p-4 border border-accent hover:bg-base-200 transition-colors">
<Icon name="arrow-right-start-on-rectangle" class="size-12 stroke-1 stroke-warning" /> <Icon name="arrow-right-start-on-rectangle" class="size-12 stroke-1 stroke-accent" />
<h3 class="text-lg font-bold">Exit</h3> <h3 class="text-lg font-bold">Exit</h3>
<p class="text-sm">Close Creddy.</p> <p class="text-sm">Close Creddy.</p>
</div> </div>
@ -71,10 +75,25 @@
{#each $appState.setupErrors as error} {#each $appState.setupErrors as error}
{#if error.show} {#if error.show}
<div class="alert alert-error shadow-lg"> <div class="alert alert-error shadow-lg">
{error.msg} <span>{error.msg}</span>
<div>
<button class="btn btn-sm btn-alert-error" on:click={() => error.show = false}>Ok</button> <button class="btn btn-sm btn-alert-error" on:click={() => error.show = false}>Ok</button>
</div> </div>
</div>
{/if} {/if}
{/each} {/each}
</div> </div>
{/if} {/if}
{#if launchTerminalError}
<div class="toast">
<div class="alert alert-error shadow-lg">
<span>{launchTerminalError.msg || launchTerminalError}</span>
<div>
<button class="btn btn-alert-error" on:click={() => launchTerminalError = null}>
Ok
</button>
</div>
</div>
</div>
{/if}

View File

@ -29,6 +29,7 @@
} }
} }
window.getOsType = type;
let osType = null; let osType = null;
type().then(t => osType = t); type().then(t => osType = t);
</script> </script>

View File

@ -17,7 +17,7 @@ module.exports = {
"primary": "#0ea5e9", "primary": "#0ea5e9",
"secondary": "#fb923c", "secondary": "#fb923c",
"accent": "#8b5cf6", "accent": "#8b5cf6",
"neutral": "#2f292c", "neutral": "#374151",
"base-100": "#252e3a", "base-100": "#252e3a",
"info": "#66cccc", "info": "#66cccc",
"success": "#52bf73", "success": "#52bf73",