show approval errors in approval view

This commit is contained in:
Joseph Montanaro 2023-05-01 16:53:24 -07:00
parent a75f34865e
commit 760987f09b
5 changed files with 88 additions and 68 deletions

View File

@ -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;

View File

@ -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>
<div class="grid grid-cols-[auto_1fr] gap-x-3">
{#each $appState.currentRequest.clients as client} {#each $appState.currentRequest.clients as client}
<p>Path: {client ? client.exe : 'Unknown'}</p> <div class="text-right">Path:</div>
<p>PID: {client ? client.pid : 'Unknown'}</p> <code class="">{@html client ? breakPath(client) : 'Unknown'}</code>
<div class="text-right">PID:</div>
<code>{client ? client.pid : 'Unknown'}</code>
{/each} {/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>
@ -64,3 +111,4 @@
</Link> </Link>
</div> </div>
</div> </div>
{/if}

View File

@ -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');

View File

@ -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,42 +11,16 @@
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 = {
id: $appState.currentRequest.id,
approval: $appState.currentRequest.approval,
};
try {
await invoke('respond', {response: packet});
success = true;
window.setTimeout( window.setTimeout(
completeRequest, completeRequest,
// Extra 50ms so the window can finish disappearing before the redraw // Extra 50ms so the window can finish disappearing before the redraw
Math.min(5000, $appState.config.rehide_ms + 50), 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 m-auto max-w-lg">
<ErrorAlert>
{error}
<svelte:fragment slot="buttons">
<Link target="Home">
<button class="btn btn-sm btn-alert-error" on:click={() => navigate('Home')}>Ok</button>
</Link>
</svelte:fragment>
</ErrorAlert>
</div>
{:else if success}
<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 max-w-max m-auto">
{#if $appState.currentRequest.approval === 'Approved'} {#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"> <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">
@ -65,4 +36,3 @@
{$appState.currentRequest.approval}! {$appState.currentRequest.approval}!
</div> </div>
</div> </div>
{/if}

View File

@ -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');