Reprompt inline on auth failure
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
4f454bc5c9356e7cc5f95d44fc58016eba0a8ac3- Parents
-
de56cb8 - Tree
b5fa32b
4f454bc
4f454bc5c9356e7cc5f95d44fc58016eba0a8ac3de56cb8
b5fa32b| Status | File | + | - |
|---|---|---|---|
| M |
garcard/src/main.rs
|
1 | 0 |
| M |
garcard/src/polkit_helper.rs
|
13 | 17 |
| M |
garcard/src/prompt.rs
|
13 | 4 |
| M |
garcard/src/prompt_ui.rs
|
10 | 7 |
garcard/src/main.rsmodified@@ -85,6 +85,7 @@ async fn main() -> Result<()> { | ||
| 85 | 85 | PromptToneArg::Success => prompt_ui::PromptTone::Success, |
| 86 | 86 | PromptToneArg::Error => prompt_ui::PromptTone::Error, |
| 87 | 87 | }, |
| 88 | + feedback_only: false, | |
| 88 | 89 | }; |
| 89 | 90 | |
| 90 | 91 | let outcome = match prompt_ui::run_prompt_dialog(request) { |
garcard/src/polkit_helper.rsmodified@@ -225,13 +225,20 @@ impl HelperSocketClient { | ||
| 225 | 225 | } |
| 226 | 226 | HelperEvent::Failure => { |
| 227 | 227 | if saw_no_session_cookie { |
| 228 | + if let Some(helper) = resolve_direct_helper_path() { | |
| 229 | + tracing::warn!( | |
| 230 | + helper = %helper.display(), | |
| 231 | + "Socket helper reported no session for cookie; falling back to direct helper process" | |
| 232 | + ); | |
| 233 | + return self.authenticate_via_helper_process_with_helper( | |
| 234 | + &helper, | |
| 235 | + &username_line, | |
| 236 | + &cookie_line, | |
| 237 | + prompts, | |
| 238 | + ); | |
| 239 | + } | |
| 228 | 240 | tracing::warn!( |
| 229 | - "Socket helper reported no session for cookie; falling back to direct helper process" | |
| 230 | - ); | |
| 231 | - return self.authenticate_via_helper_process( | |
| 232 | - &username_line, | |
| 233 | - &cookie_line, | |
| 234 | - prompts, | |
| 241 | + "Socket helper reported no session for cookie and no viable direct helper is available; treating as authentication failure" | |
| 235 | 242 | ); |
| 236 | 243 | } |
| 237 | 244 | prompts |
@@ -243,17 +250,6 @@ impl HelperSocketClient { | ||
| 243 | 250 | } |
| 244 | 251 | } |
| 245 | 252 | |
| 246 | - fn authenticate_via_helper_process<P: PromptProvider>( | |
| 247 | - &self, | |
| 248 | - username: &str, | |
| 249 | - cookie: &str, | |
| 250 | - prompts: &mut P, | |
| 251 | - ) -> Result<HelperOutcome> { | |
| 252 | - let helper = resolve_direct_helper_path() | |
| 253 | - .context("failed to locate direct polkit helper binary for socket fallback")?; | |
| 254 | - self.authenticate_via_helper_process_with_helper(&helper, username, cookie, prompts) | |
| 255 | - } | |
| 256 | - | |
| 257 | 253 | fn authenticate_via_helper_process_with_helper<P: PromptProvider>( |
| 258 | 254 | &self, |
| 259 | 255 | helper: &Path, |
garcard/src/prompt.rsmodified@@ -33,6 +33,7 @@ pub struct CommandPrompt { | ||
| 33 | 33 | prompt_timeout_secs: u64, |
| 34 | 34 | session: Option<PromptSession>, |
| 35 | 35 | session_unavailable: bool, |
| 36 | + next_prompt_tone: Option<PromptTone>, | |
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | impl Default for CommandPrompt { |
@@ -48,12 +49,15 @@ impl Default for CommandPrompt { | ||
| 48 | 49 | prompt_timeout_secs, |
| 49 | 50 | session: None, |
| 50 | 51 | session_unavailable: false, |
| 52 | + next_prompt_tone: None, | |
| 51 | 53 | } |
| 52 | 54 | } |
| 53 | 55 | } |
| 54 | 56 | |
| 55 | 57 | impl CommandPrompt { |
| 56 | 58 | fn run_prompt(&mut self, prompt: &str, visible: bool) -> Result<PromptResponse> { |
| 59 | + let tone = self.next_prompt_tone.take().unwrap_or(PromptTone::Default); | |
| 60 | + | |
| 57 | 61 | if let Some(command) = self.prompt_command.as_deref() { |
| 58 | 62 | tracing::debug!(visible, "Using custom prompt command backend"); |
| 59 | 63 | return run_custom_prompt_command(command, prompt, visible); |
@@ -71,7 +75,8 @@ impl CommandPrompt { | ||
| 71 | 75 | PromptMode::Secret |
| 72 | 76 | }, |
| 73 | 77 | timeout_secs, |
| 74 | - tone: UiPromptTone::Default, | |
| 78 | + tone: to_ui_tone(tone), | |
| 79 | + feedback_only: false, | |
| 75 | 80 | }; |
| 76 | 81 | let started = Instant::now(); |
| 77 | 82 | Some((session.run(request), started.elapsed())) |
@@ -106,7 +111,7 @@ impl CommandPrompt { | ||
| 106 | 111 | } |
| 107 | 112 | } |
| 108 | 113 | |
| 109 | - match run_gartk_prompt_subcommand(prompt, visible, timeout_secs) { | |
| 114 | + match run_gartk_prompt_subcommand(prompt, visible, timeout_secs, tone) { | |
| 110 | 115 | Ok(response) => { |
| 111 | 116 | tracing::debug!(visible, response = ?response, "Prompt subprocess completed"); |
| 112 | 117 | Ok(response) |
@@ -189,11 +194,14 @@ impl PromptProvider for CommandPrompt { | ||
| 189 | 194 | } |
| 190 | 195 | |
| 191 | 196 | fn auth_succeeded(&mut self) -> Result<()> { |
| 197 | + self.next_prompt_tone = None; | |
| 192 | 198 | self.run_feedback("Authentication succeeded", PromptTone::Success) |
| 193 | 199 | } |
| 194 | 200 | |
| 195 | 201 | fn auth_failed(&mut self, message: &str) -> Result<()> { |
| 196 | - self.run_feedback(message, PromptTone::Error) | |
| 202 | + tracing::warn!("polkit helper message: {}", message); | |
| 203 | + self.next_prompt_tone = Some(PromptTone::Error); | |
| 204 | + Ok(()) | |
| 197 | 205 | } |
| 198 | 206 | } |
| 199 | 207 | |
@@ -224,6 +232,7 @@ fn run_gartk_prompt_subcommand( | ||
| 224 | 232 | prompt: &str, |
| 225 | 233 | visible: bool, |
| 226 | 234 | timeout_secs: u64, |
| 235 | + tone: PromptTone, | |
| 227 | 236 | ) -> Result<PromptResponse> { |
| 228 | 237 | let mode = if visible { "plain" } else { "secret" }; |
| 229 | 238 | let executable = |
@@ -237,7 +246,7 @@ fn run_gartk_prompt_subcommand( | ||
| 237 | 246 | .arg("--timeout-secs") |
| 238 | 247 | .arg(timeout_secs.to_string()) |
| 239 | 248 | .arg("--tone") |
| 240 | - .arg(PromptTone::Default.as_arg()) | |
| 249 | + .arg(tone.as_arg()) | |
| 241 | 250 | .output() |
| 242 | 251 | .context("failed to launch garcard prompt subcommand")?; |
| 243 | 252 | |
garcard/src/prompt_ui.rsmodified@@ -1,9 +1,9 @@ | ||
| 1 | 1 | use anyhow::{Context, Result}; |
| 2 | 2 | use gartk_core::{Color, InputEvent, Key, KeyEvent, Rect, Theme}; |
| 3 | -use gartk_render::{copy_surface_to_window, Renderer, TextStyle}; | |
| 3 | +use gartk_render::{Renderer, TextStyle, copy_surface_to_window}; | |
| 4 | 4 | use gartk_x11::{ |
| 5 | - monitor_at_pointer, primary_monitor, Connection, EventLoop, EventLoopConfig, Window, | |
| 6 | - WindowConfig, | |
| 5 | + Connection, EventLoop, EventLoopConfig, Window, WindowConfig, monitor_at_pointer, | |
| 6 | + primary_monitor, | |
| 7 | 7 | }; |
| 8 | 8 | use std::time::{Duration, Instant}; |
| 9 | 9 | use x11rb::connection::Connection as X11Connection; |
@@ -33,6 +33,7 @@ pub struct PromptRequest { | ||
| 33 | 33 | pub mode: PromptMode, |
| 34 | 34 | pub timeout_secs: u64, |
| 35 | 35 | pub tone: PromptTone, |
| 36 | + pub feedback_only: bool, | |
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -152,6 +153,7 @@ impl PromptSession { | ||
| 152 | 153 | mode: PromptMode::Secret, |
| 153 | 154 | timeout_secs: 0, |
| 154 | 155 | tone: PromptTone::Default, |
| 156 | + feedback_only: false, | |
| 155 | 157 | }; |
| 156 | 158 | |
| 157 | 159 | let conn = Connection::connect(None).context("failed to connect to X11 display")?; |
@@ -188,6 +190,7 @@ impl PromptSession { | ||
| 188 | 190 | mode: PromptMode::Plain, |
| 189 | 191 | timeout_secs, |
| 190 | 192 | tone, |
| 193 | + feedback_only: true, | |
| 191 | 194 | })?; |
| 192 | 195 | Ok(()) |
| 193 | 196 | } |
@@ -377,7 +380,7 @@ impl PromptDialog { | ||
| 377 | 380 | } |
| 378 | 381 | |
| 379 | 382 | fn handle_key(&mut self, key_event: &KeyEvent) { |
| 380 | - if self.request.tone != PromptTone::Default { | |
| 383 | + if self.request.feedback_only { | |
| 381 | 384 | // Feedback dialogs are transient; ignore keypresses so the submit key |
| 382 | 385 | // from the previous prompt cannot dismiss success/error feedback early. |
| 383 | 386 | return; |
@@ -547,10 +550,10 @@ impl PromptDialog { | ||
| 547 | 550 | .font_family(theme.font_family) |
| 548 | 551 | .font_size(12.0) |
| 549 | 552 | .color(theme.item_description); |
| 550 | - let footer_text = if self.request.tone == PromptTone::Default { | |
| 551 | - "Enter submit Esc cancel" | |
| 552 | - } else { | |
| 553 | + let footer_text = if self.request.feedback_only { | |
| 553 | 554 | "Please wait" |
| 555 | + } else { | |
| 556 | + "Enter submit Esc cancel" | |
| 554 | 557 | }; |
| 555 | 558 | self.renderer.text( |
| 556 | 559 | footer_text, |