diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d942cce..14d37ee 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -13,7 +13,7 @@ use k8s_openapi::api::core::v1::{ ConfigMap, Namespace, PersistentVolume, PersistentVolumeClaim, Pod, Secret, Service, }; use kube::api::{DeleteParams, ListParams}; -use kube::config::{KubeConfigOptions, Kubeconfig, KubeconfigError}; +use kube::config::{KubeConfigOptions, Kubeconfig, KubeconfigError, NamedAuthInfo}; use kube::{api::Api, Client, Config, Error}; use portable_pty::{native_pty_system, CommandBuilder, PtySize}; use serde::Serialize; @@ -107,6 +107,36 @@ async fn list_contexts() -> Result, SerializableKubeError> { .collect() } +#[tauri::command] +async fn get_context_auth_info(context: &str) -> Result { + let config = Kubeconfig::read().map_err(|err| SerializableKubeError::from(err))?; + + let context_auth_info = config + .contexts + .iter() + .find(|c| c.name == context) + .map(|c| c.clone().context.unwrap().user.clone()) + .ok_or(SerializableKubeError { + message: "Context not found".to_string(), + code: None, + reason: None, + details: None, + })?; + + let auth_info = config + .auth_infos + .iter() + .find(|a| a.name == context_auth_info) + .ok_or(SerializableKubeError { + message: "Auth info not found".to_string(), + code: None, + reason: None, + details: None, + })?; + + return Ok(auth_info.clone()); +} + async fn client_with_context(context: &str) -> Result { if context.to_string() != CURRENT_CONTEXT.lock().unwrap().as_ref().unwrap().clone() { let options = KubeConfigOptions { @@ -663,7 +693,7 @@ fn write_to_pty(session_id: &str, data: &str) { } } -fn main() { +fn main() { let _ = fix_path_env::fix(); let metadata = AboutMetadata::new() @@ -698,6 +728,7 @@ fn main() { }) .invoke_handler(tauri::generate_handler![ list_contexts, + get_context_auth_info, get_current_context, list_namespaces, get_core_api_versions, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 4390f67..6633bcc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -36,6 +36,11 @@ "name": "kubectl", "cmd": "kubectl", "args": true + }, + { + "name": "aws", + "cmd": "aws", + "args": true } ] } diff --git a/src/App.vue b/src/App.vue index 0c9a1bf..357803d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -21,20 +21,20 @@ import UpdateHandler from "./components/UpdateHandler.vue"; - - - - + + + + - - - - + + + + diff --git a/src/components/ContextSwitcher.vue b/src/components/ContextSwitcher.vue index 3f2558c..6d71138 100644 --- a/src/components/ContextSwitcher.vue +++ b/src/components/ContextSwitcher.vue @@ -4,6 +4,8 @@ import { injectStrict } from "@/lib/utils"; import { ShowSingleCommandKey, RegisterCommandStateKey, + CloseCommandPaletteKey, + RerunLastCommandKey, } from "@/providers/CommandPaletteProvider"; import { KubeContextSetContextKey, @@ -12,13 +14,17 @@ import { import { KubeContextStateKey } from "@/providers/KubeContextProvider"; import { Kubernetes } from "@/services/Kubernetes"; import { SettingsContextStateKey } from "@/providers/SettingsContextProvider"; +import { DialogProviderSpawnDialogKey } from "@/providers/DialogProvider"; const showSingleCommand = injectStrict(ShowSingleCommandKey); const registerCommand = injectStrict(RegisterCommandStateKey); +const closeCommandPalette = injectStrict(CloseCommandPaletteKey); +const rerunLastCommand = injectStrict(RerunLastCommandKey); const { context, namespace } = injectStrict(KubeContextStateKey); const setContext = injectStrict(KubeContextSetContextKey); const setNamespace = injectStrict(KubeContextSetNamespaceKey); const { settings } = injectStrict(SettingsContextStateKey); +const spawnDialog = injectStrict(DialogProviderSpawnDialogKey); onMounted(() => { registerCommand({ @@ -38,7 +44,7 @@ onMounted(() => { (c) => c.context === context ); - let namespaces = []; + let namespaces: string[] = []; if ( clusterSettings && @@ -47,9 +53,49 @@ onMounted(() => { ) { namespaces = clusterSettings.namespaces; } else { - namespaces = await ( - await Kubernetes.getNamespaces(context) - ).map((ns) => ns.metadata?.name || ""); + try { + const contextNamespaces = await Kubernetes.getNamespaces(context); + namespaces = contextNamespaces.map( + (ns) => ns.metadata?.name || "" + ); + } catch (e: any) { + const authErrorHandler = await Kubernetes.getAuthErrorHandler( + context, + e.message + ); + + if (authErrorHandler.canHandle) { + spawnDialog({ + title: "SSO Session expired", + message: + "Failed to authenticate as the SSO session has expired. Please login again.", + buttons: [ + { + label: "Close", + variant: "ghost", + handler: (dialog) => { + dialog.close(); + closeCommandPalette(); + }, + }, + { + label: "Login with SSO", + handler: (dialog) => { + dialog.buttons = []; + dialog.title = "Awaiting SSO login"; + dialog.message = "Please wait while we redirect you."; + authErrorHandler.callback(() => { + dialog.close(); + rerunLastCommand(); + }); + }, + }, + ], + }); + } else { + throw e; + } + } } return [ diff --git a/src/components/DialogHandler.vue b/src/components/DialogHandler.vue index 01685df..81ffe44 100644 --- a/src/components/DialogHandler.vue +++ b/src/components/DialogHandler.vue @@ -1,14 +1,13 @@