show approval errors in approval view
This commit is contained in:
		| @@ -10,6 +10,7 @@ export let appState = writable({ | |||||||
|     credentialStatus: 'locked', |     credentialStatus: 'locked', | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  |  | ||||||
| export async function acceptRequest() { | export async function acceptRequest() { | ||||||
|     let req = await get(appState).pendingRequests.get(); |     let req = await get(appState).pendingRequests.get(); | ||||||
|     appState.update($appState => { |     appState.update($appState => { | ||||||
| @@ -20,6 +21,7 @@ export async function acceptRequest() { | |||||||
|     navigate('Approve'); |     navigate('Approve'); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| export function completeRequest() { | export function completeRequest() { | ||||||
|     appState.update($appState => { |     appState.update($appState => { | ||||||
|         $appState.currentRequest = null; |         $appState.currentRequest = null; | ||||||
|   | |||||||
| @@ -1,17 +1,35 @@ | |||||||
| <script> | <script> | ||||||
|  |     import { onMount } from 'svelte'; | ||||||
|     import { invoke } from '@tauri-apps/api/tauri'; |     import { invoke } from '@tauri-apps/api/tauri'; | ||||||
|  |  | ||||||
|     import { navigate } from '../lib/routing.js'; |     import { navigate } from '../lib/routing.js'; | ||||||
|     import { appState } from '../lib/state.js'; |     import { appState, completeRequest } from '../lib/state.js'; | ||||||
|  |     import ErrorAlert from '../ui/ErrorAlert.svelte'; | ||||||
|     import Link from '../ui/Link.svelte'; |     import Link from '../ui/Link.svelte'; | ||||||
|     import Icon from '../ui/Icon.svelte'; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     // Send response to backend, display error if applicable | ||||||
|  |     let error, alert; | ||||||
|  |     async function respond() { | ||||||
|  |         let {id, approval} = $appState.currentRequest; | ||||||
|  |         try { | ||||||
|  |             await invoke('respond', {response: {id, approval}}); | ||||||
|  |             navigate('ShowResponse'); | ||||||
|  |         } | ||||||
|  |         catch (e) { | ||||||
|  |             if (error) { | ||||||
|  |                 alert.shake(); | ||||||
|  |             } | ||||||
|  |             error = e; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Approval has one of several outcomes depending on current credential state | ||||||
|     async function approve() { |     async function approve() { | ||||||
|         $appState.currentRequest.approval = 'Approved'; |         $appState.currentRequest.approval = 'Approved'; | ||||||
|         let status = await invoke('get_session_status'); |         let status = await invoke('get_session_status'); | ||||||
|         if (status === 'unlocked') { |         if (status === 'unlocked') { | ||||||
|             navigate('ShowResponse'); |             await respond(); | ||||||
|         } |         } | ||||||
|         else if (status === 'locked') { |         else if (status === 'locked') { | ||||||
|             navigate('Unlock'); |             navigate('Unlock'); | ||||||
| @@ -21,40 +39,69 @@ | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function deny() { |     // Denial has only one | ||||||
|  |     async function deny() { | ||||||
|         $appState.currentRequest.approval = 'Denied'; |         $appState.currentRequest.approval = 'Denied'; | ||||||
|         navigate('ShowResponse'); |         await respond(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Extract executable name from full path | ||||||
|     let appName = null; |     let appName = null; | ||||||
|     if ($appState.currentRequest.clients.length === 1) { |     if ($appState.currentRequest.clients.length === 1) { | ||||||
|         let path = $appState.currentRequest.clients[0].exe; |         let path = $appState.currentRequest.clients[0].exe; | ||||||
|         // grab the filename from the path |  | ||||||
|         let m = path.match(/\/([^/]+?$)|\\([^\\]+?$)/); |         let m = path.match(/\/([^/]+?$)|\\([^\\]+?$)/); | ||||||
|         appName = m[1] || m[2]; |         appName = m[1] || m[2]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Executable paths can be long, so ensure they only break on \ or / | ||||||
|  |     function breakPath(client) { | ||||||
|  |         return client.exe.replace(/(\\|\/)/g, '$1<wbr>'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // if the request has already been approved/denied, send response immediately | ||||||
|  |     onMount(async () => { | ||||||
|  |         if ($appState.currentRequest.approval) { | ||||||
|  |             await respond(); | ||||||
|  |         } | ||||||
|  |     }) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  |  | ||||||
| <div class="flex flex-col space-y-4 p-4 m-auto max-w-max h-screen justify-center"> | <!-- Don't render at all if we're just going to immediately proceed to the next screen --> | ||||||
|     <!-- <div class="p-4 rounded-box border-2 border-neutral-content"> --> | {#if !$appState.currentRequest.approval} | ||||||
|  |     <div class="flex flex-col space-y-4 p-4 m-auto max-w-xl h-screen items-center justify-center"> | ||||||
|  |         {#if error} | ||||||
|  |             <ErrorAlert bind:this={alert}> | ||||||
|  |                 {error} | ||||||
|  |                 <svelte:fragment slot="buttons"> | ||||||
|  |                     <button class="btn btn-sm btn-alert-error" on:click={completeRequest}>Cancel</button> | ||||||
|  |                     <button class="btn btn-sm btn-alert-error" on:click={respond}>Retry</button> | ||||||
|  |                 </svelte:fragment> | ||||||
|  |             </ErrorAlert> | ||||||
|  |         {/if} | ||||||
|  |  | ||||||
|         <div class="space-y-1 mb-4"> |         <div class="space-y-1 mb-4"> | ||||||
|             <h2 class="text-xl font-bold">{appName ? `"${appName}"` : 'An appplication'} would like to access your AWS credentials.</h2> |             <h2 class="text-xl font-bold">{appName ? `"${appName}"` : 'An appplication'} would like to access your AWS credentials.</h2> | ||||||
|             {#each $appState.currentRequest.clients as client} |  | ||||||
|                 <p>Path: {client ? client.exe : 'Unknown'}</p> |             <div class="grid grid-cols-[auto_1fr] gap-x-3"> | ||||||
|                 <p>PID: {client ? client.pid : 'Unknown'}</p> |                 {#each $appState.currentRequest.clients as client} | ||||||
|             {/each} |                     <div class="text-right">Path:</div> | ||||||
|  |                     <code class="">{@html client ? breakPath(client) : 'Unknown'}</code> | ||||||
|  |                     <div class="text-right">PID:</div> | ||||||
|  |                     <code>{client ? client.pid : 'Unknown'}</code> | ||||||
|  |                 {/each} | ||||||
|  |             </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <div class="grid grid-cols-2"> |         <div class="w-full flex justify-between"> | ||||||
|             <Link target="{deny}" hotkey="Escape"> |             <Link target={deny} hotkey="Escape"> | ||||||
|                 <button class="btn btn-error justify-self-start"> |                 <button class="btn btn-error justify-self-start"> | ||||||
|                     Deny |                     Deny | ||||||
|                     <kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Esc</kbd> |                     <kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Esc</kbd> | ||||||
|                 </button> |                 </button> | ||||||
|             </Link> |             </Link> | ||||||
|  |  | ||||||
|             <Link target="{approve}" hotkey="Enter" shift="{true}"> |             <Link target={approve} hotkey="Enter" shift="{true}"> | ||||||
|                 <button class="btn btn-success justify-self-end"> |                 <button class="btn btn-success justify-self-end"> | ||||||
|                     Approve |                     Approve | ||||||
|                     <kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Shift</kbd> |                     <kbd class="ml-2 normal-case px-1 py-0.5 rounded border border-neutral">Shift</kbd> | ||||||
| @@ -63,4 +110,5 @@ | |||||||
|                 </button> |                 </button> | ||||||
|             </Link> |             </Link> | ||||||
|         </div> |         </div> | ||||||
| </div> |     </div> | ||||||
|  | {/if} | ||||||
| @@ -29,7 +29,7 @@ | |||||||
|         try { |         try { | ||||||
|             await invoke('save_credentials', {credentials, passphrase}); |             await invoke('save_credentials', {credentials, passphrase}); | ||||||
|             if ($appState.currentRequest) { |             if ($appState.currentRequest) { | ||||||
|                 navigate('ShowResponse'); |                 navigate('Approve'); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 navigate('Home'); |                 navigate('Home'); | ||||||
|   | |||||||
| @@ -1,11 +1,8 @@ | |||||||
| <script> | <script> | ||||||
|     import { onMount } from 'svelte'; |     import { onMount } from 'svelte'; | ||||||
|     import { draw, fade } from 'svelte/transition'; |     import { draw, fade } from 'svelte/transition'; | ||||||
|     import { invoke } from '@tauri-apps/api/tauri'; |  | ||||||
|  |  | ||||||
|     import { appState, completeRequest } from '../lib/state.js'; |     import { appState, completeRequest } from '../lib/state.js'; | ||||||
|     import ErrorAlert from '../ui/ErrorAlert.svelte'; |  | ||||||
|     import Link from '../ui/Link.svelte'; |  | ||||||
|      |      | ||||||
|     let success = false; |     let success = false; | ||||||
|     let error = null; |     let error = null; | ||||||
| @@ -14,55 +11,28 @@ | |||||||
|     let fadeDuration = drawDuration * 0.6; |     let fadeDuration = drawDuration * 0.6; | ||||||
|     let fadeDelay = drawDuration * 0.4; |     let fadeDelay = drawDuration * 0.4; | ||||||
|  |  | ||||||
|     async function respond() { |     onMount(() => { | ||||||
|         let packet = { |         window.setTimeout( | ||||||
|             id: $appState.currentRequest.id, |             completeRequest, | ||||||
|             approval: $appState.currentRequest.approval, |             // Extra 50ms so the window can finish disappearing before the redraw | ||||||
|         }; |             Math.min(5000, $appState.config.rehide_ms + 50), | ||||||
|  |         ) | ||||||
|         try { |     }) | ||||||
|             await invoke('respond', {response: packet}); |  | ||||||
|             success = true; |  | ||||||
|             window.setTimeout( |  | ||||||
|                 completeRequest, |  | ||||||
|                 // Extra 50ms so the window can finish disappearing before the redraw |  | ||||||
|                 Math.min(5000, $appState.config.rehide_ms + 50), |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         catch (e) { |  | ||||||
|             error = e; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|     onMount(respond); |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  |  | ||||||
| {#if error} | <div class="flex flex-col h-screen items-center justify-center max-w-max m-auto"> | ||||||
|     <div class="flex flex-col h-screen items-center justify-center m-auto max-w-lg"> |     {#if $appState.currentRequest.approval === 'Approved'} | ||||||
|         <ErrorAlert> |         <svg xmlns="http://www.w3.org/2000/svg" class="w-36 h-36" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||||||
|             {error} |           <path in:draw="{{duration: drawDuration}}" stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||||
|             <svelte:fragment slot="buttons"> |         </svg> | ||||||
|                 <Link target="Home"> |     {:else} | ||||||
|                     <button class="btn btn-sm btn-alert-error" on:click={() => navigate('Home')}>Ok</button> |         <svg xmlns="http://www.w3.org/2000/svg" class="w-36 h-36" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> | ||||||
|                 </Link> |             <path in:draw="{{duration: 500}}" stroke-linecap="round" stroke-linejoin="round" d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||||
|             </svelte:fragment> |         </svg> | ||||||
|         </ErrorAlert> |     {/if} | ||||||
|     </div> |  | ||||||
| {:else if success} |  | ||||||
|     <div class="flex flex-col h-screen items-center justify-center max-w-max m-auto"> |  | ||||||
|         {#if $appState.currentRequest.approval === 'Approved'} |  | ||||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="w-36 h-36" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> |  | ||||||
|               <path in:draw="{{duration: drawDuration}}" stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |  | ||||||
|             </svg> |  | ||||||
|         {:else} |  | ||||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="w-36 h-36" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor"> |  | ||||||
|                 <path in:draw="{{duration: 500}}" stroke-linecap="round" stroke-linejoin="round" d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |  | ||||||
|             </svg> |  | ||||||
|         {/if} |  | ||||||
|  |  | ||||||
|         <div in:fade="{{duration: fadeDuration, delay: fadeDelay}}" class="text-2xl font-bold"> |     <div in:fade="{{duration: fadeDuration, delay: fadeDelay}}" class="text-2xl font-bold"> | ||||||
|             {$appState.currentRequest.approval}! |         {$appState.currentRequest.approval}! | ||||||
|         </div> |  | ||||||
|     </div> |     </div> | ||||||
| {/if} | </div> | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ | |||||||
|             let r = await invoke('unlock', {passphrase}); |             let r = await invoke('unlock', {passphrase}); | ||||||
|             $appState.credentialStatus = 'unlocked'; |             $appState.credentialStatus = 'unlocked'; | ||||||
|             if ($appState.currentRequest) { |             if ($appState.currentRequest) { | ||||||
|                 navigate('ShowResponse'); |                 navigate('Approve'); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 navigate('Home'); |                 navigate('Home'); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user