gardesk/garcard / 517f24c

Browse files

process auth queue via polkit helper

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
517f24c7914bfe6e8291c15306a73cd51a58a85a
Parents
81f4555
Tree
559dc1d

7 changed files

StatusFile+-
M README.md 5 0
M garcard/src/agent.rs 245 6
M garcard/src/daemon.rs 10 6
M garcard/src/main.rs 1 0
M garcard/src/polkit_helper.rs 8 2
A garcard/src/prompt.rs 110 0
M garcard/src/state.rs 20 30
README.mdmodified
@@ -21,5 +21,10 @@ Environment overrides:
2121
 4. `GARCARD_AGENT_BACKEND`
2222
 5. `GARCARD_POLKIT_OBJECT_PATH`
2323
 6. `GARCARD_LOCALE`
24
+7. `GARCARD_POLKIT_HELPER_SOCKET`
25
+8. `GARCARD_PROMPT_COMMAND`
2426
 
2527
 See `examples/config.toml` for a starter file.
28
+
29
+`GARCARD_PROMPT_COMMAND` is optional. If unset, `garcard` falls back to
30
+`systemd-ask-password` for prompt interaction.
garcard/src/agent.rsmodified
@@ -1,7 +1,12 @@
1
+use crate::polkit_helper::{DEFAULT_HELPER_SOCKET, HelperOutcome, HelperSocketClient};
2
+use crate::prompt::CommandPrompt;
13
 use crate::state::{AuthPhase, AuthQueue, AuthState, QueueInsert};
24
 use anyhow::{Context, Result};
35
 use std::collections::HashMap;
6
+use std::path::PathBuf;
7
+use std::sync::atomic::{AtomicBool, Ordering};
48
 use std::sync::{Arc, Mutex};
9
+use std::thread;
510
 use zbus::blocking::{Connection, Proxy};
611
 use zbus::fdo;
712
 use zbus::zvariant::{OwnedObjectPath, OwnedValue};
@@ -53,21 +58,49 @@ struct AuthRequest {
5358
     identities: Vec<Subject>,
5459
 }
5560
 
61
+#[derive(Debug, Clone)]
62
+struct ActiveRequest {
63
+    action_id: String,
64
+    message: String,
65
+    icon_name: String,
66
+    detail_count: usize,
67
+    cookie: String,
68
+    username: String,
69
+}
70
+
5671
 #[derive(Debug)]
5772
 struct PolkitRuntime {
5873
     auth_state: Arc<AuthState>,
5974
     queue: Mutex<AuthQueue<AuthRequest>>,
75
+    helper_client: HelperSocketClient,
76
+    processing: AtomicBool,
77
+    worker_enabled: bool,
6078
 }
6179
 
6280
 impl PolkitRuntime {
6381
     fn new(auth_state: Arc<AuthState>) -> Self {
82
+        Self::new_with_worker(auth_state, true)
83
+    }
84
+
85
+    #[cfg(test)]
86
+    fn new_without_worker(auth_state: Arc<AuthState>) -> Self {
87
+        Self::new_with_worker(auth_state, false)
88
+    }
89
+
90
+    fn new_with_worker(auth_state: Arc<AuthState>, worker_enabled: bool) -> Self {
91
+        let helper_socket = std::env::var_os("GARCARD_POLKIT_HELPER_SOCKET")
92
+            .map(PathBuf::from)
93
+            .unwrap_or_else(|| PathBuf::from(DEFAULT_HELPER_SOCKET));
6494
         Self {
6595
             auth_state,
6696
             queue: Mutex::new(AuthQueue::default()),
97
+            helper_client: HelperSocketClient::new(helper_socket),
98
+            processing: AtomicBool::new(false),
99
+            worker_enabled,
67100
         }
68101
     }
69102
 
70
-    fn begin_authentication(&self, request: AuthRequest) -> Result<QueueInsert> {
103
+    fn begin_authentication(self: &Arc<Self>, request: AuthRequest) -> Result<QueueInsert> {
71104
         let (insert, active, queued) = {
72105
             let mut queue = self
73106
                 .queue
@@ -90,10 +123,14 @@ impl PolkitRuntime {
90123
             self.auth_state.set_phase(AuthPhase::PendingPrompt);
91124
         }
92125
 
126
+        if self.worker_enabled && active > 0 {
127
+            self.ensure_worker();
128
+        }
129
+
93130
         Ok(insert)
94131
     }
95132
 
96
-    fn cancel_authentication(&self, cookie: &str) -> Result<bool> {
133
+    fn cancel_authentication(self: &Arc<Self>, cookie: &str) -> Result<bool> {
97134
         let (canceled_active, removed_queued, active, queued) = {
98135
             let mut queue = self
99136
                 .queue
@@ -118,6 +155,9 @@ impl PolkitRuntime {
118155
             } else {
119156
                 self.auth_state.set_phase(AuthPhase::Canceled);
120157
             }
158
+            if self.worker_enabled && active > 0 {
159
+                self.ensure_worker();
160
+            }
121161
             return Ok(true);
122162
         }
123163
 
@@ -131,15 +171,190 @@ impl PolkitRuntime {
131171
         Ok(false)
132172
     }
133173
 
174
+    fn ensure_worker(self: &Arc<Self>) {
175
+        if self.processing.swap(true, Ordering::AcqRel) {
176
+            return;
177
+        }
178
+
179
+        let runtime = Arc::clone(self);
180
+        let spawn_result = thread::Builder::new()
181
+            .name("garcard-auth-worker".to_string())
182
+            .spawn(move || runtime.process_loop());
183
+        if let Err(err) = spawn_result {
184
+            self.processing.store(false, Ordering::Release);
185
+            tracing::error!(error = %err, "Failed to spawn garcard auth worker");
186
+        }
187
+    }
188
+
189
+    fn process_loop(self: Arc<Self>) {
190
+        loop {
191
+            let Some(active) = self.active_request_snapshot() else {
192
+                break;
193
+            };
194
+
195
+            self.auth_state.set_phase(AuthPhase::Verifying);
196
+            tracing::info!(
197
+                action_id = %active.action_id,
198
+                icon_name = %active.icon_name,
199
+                detail_count = active.detail_count,
200
+                "Processing polkit auth request"
201
+            );
202
+
203
+            let outcome = self.authenticate_active_request(&active);
204
+            self.complete_request(&active.cookie, outcome);
205
+        }
206
+
207
+        self.processing.store(false, Ordering::Release);
208
+        if self.has_active_request() {
209
+            self.ensure_worker();
210
+        }
211
+    }
212
+
213
+    fn has_active_request(&self) -> bool {
214
+        self.queue
215
+            .lock()
216
+            .map(|queue| queue.active().is_some())
217
+            .unwrap_or(false)
218
+    }
219
+
220
+    fn active_request_snapshot(&self) -> Option<ActiveRequest> {
221
+        let queue = self.queue.lock().ok()?;
222
+        let request = queue.active()?;
223
+
224
+        let username = resolve_identity_username(&request.identities)
225
+            .or_else(current_username)
226
+            .or_else(|| std::env::var("USER").ok())
227
+            .unwrap_or_else(|| "unknown".to_string());
228
+
229
+        Some(ActiveRequest {
230
+            action_id: request.action_id.clone(),
231
+            message: request.message.clone(),
232
+            icon_name: request.icon_name.clone(),
233
+            detail_count: request.details.len(),
234
+            cookie: request.cookie.clone(),
235
+            username,
236
+        })
237
+    }
238
+
239
+    fn authenticate_active_request(&self, request: &ActiveRequest) -> HelperOutcome {
240
+        let mut prompts = CommandPrompt::default();
241
+        let prompt_context = if request.message.is_empty() {
242
+            request.action_id.as_str()
243
+        } else {
244
+            request.message.as_str()
245
+        };
246
+        tracing::info!(context = %prompt_context, "Starting helper authentication dialog");
247
+
248
+        match self
249
+            .helper_client
250
+            .authenticate(&request.username, &request.cookie, &mut prompts)
251
+        {
252
+            Ok(outcome) => outcome,
253
+            Err(err) => {
254
+                tracing::warn!(
255
+                    action_id = %request.action_id,
256
+                    error = %err,
257
+                    "Polkit helper authentication failed"
258
+                );
259
+                HelperOutcome::Denied
260
+            }
261
+        }
262
+    }
263
+
264
+    fn complete_request(&self, cookie: &str, outcome: HelperOutcome) {
265
+        let (removed, active, queued) = {
266
+            let mut queue = match self.queue.lock() {
267
+                Ok(queue) => queue,
268
+                Err(_) => {
269
+                    tracing::error!("auth request queue lock poisoned");
270
+                    return;
271
+                }
272
+            };
273
+            let removed = queue
274
+                .complete_active_if(|request| request.cookie == cookie)
275
+                .is_some();
276
+            let (active, queued) = queue.counts();
277
+            (removed, active, queued)
278
+        };
279
+
280
+        self.auth_state.sync_queue_counts(active, queued);
281
+        if !removed {
282
+            if active == 0 && queued == 0 && self.auth_state.phase() == AuthPhase::Verifying {
283
+                self.auth_state.set_phase(AuthPhase::Idle);
284
+            }
285
+            return;
286
+        }
287
+
288
+        if active > 0 {
289
+            self.auth_state.set_phase(AuthPhase::PendingPrompt);
290
+            return;
291
+        }
292
+
293
+        let phase = match outcome {
294
+            HelperOutcome::Authorized => AuthPhase::Success,
295
+            HelperOutcome::Denied => AuthPhase::Failure,
296
+            HelperOutcome::Canceled => AuthPhase::Canceled,
297
+        };
298
+        self.auth_state.set_phase(phase);
299
+    }
300
+
134301
     fn reset(&self) {
135302
         if let Ok(mut queue) = self.queue.lock() {
136303
             queue.clear();
137304
         }
305
+        self.processing.store(false, Ordering::Release);
138306
         self.auth_state.set_phase(AuthPhase::Idle);
139307
         self.auth_state.sync_queue_counts(0, 0);
140308
     }
141309
 }
142310
 
311
+fn resolve_identity_username(identities: &[Subject]) -> Option<String> {
312
+    for (kind, details) in identities {
313
+        if kind != "unix-user" {
314
+            continue;
315
+        }
316
+
317
+        if let Some(value) = details.get("name") {
318
+            if let Ok(name) = <&str>::try_from(value) {
319
+                return Some(name.to_string());
320
+            }
321
+        }
322
+
323
+        if let Some(uid) = details.get("uid").and_then(parse_uid) {
324
+            if let Some(name) = username_for_uid(uid) {
325
+                return Some(name);
326
+            }
327
+        }
328
+    }
329
+
330
+    None
331
+}
332
+
333
+fn parse_uid(value: &OwnedValue) -> Option<u32> {
334
+    if let Ok(uid) = u32::try_from(value) {
335
+        return Some(uid);
336
+    }
337
+    if let Ok(uid) = u64::try_from(value) {
338
+        return u32::try_from(uid).ok();
339
+    }
340
+    if let Ok(uid) = i32::try_from(value) {
341
+        return u32::try_from(uid).ok();
342
+    }
343
+
344
+    None
345
+}
346
+
347
+fn username_for_uid(uid: u32) -> Option<String> {
348
+    nix::unistd::User::from_uid(nix::unistd::Uid::from_raw(uid))
349
+        .ok()
350
+        .flatten()
351
+        .map(|user| user.name)
352
+}
353
+
354
+fn current_username() -> Option<String> {
355
+    username_for_uid(nix::unistd::geteuid().as_raw())
356
+}
357
+
143358
 #[derive(Debug, Clone)]
144359
 struct PolkitAuthAgentObject {
145360
     runtime: Arc<PolkitRuntime>,
@@ -162,6 +377,8 @@ impl PolkitAuthAgentObject {
162377
         cookie: &str,
163378
         identities: Vec<Subject>,
164379
     ) -> fdo::Result<()> {
380
+        let detail_count = details.len();
381
+        let identity_count = identities.len();
165382
         let request = AuthRequest {
166383
             action_id: action_id.to_string(),
167384
             message: message.to_string(),
@@ -178,11 +395,20 @@ impl PolkitAuthAgentObject {
178395
 
179396
         match queue_insert {
180397
             QueueInsert::Activated => {
181
-                tracing::info!(action_id = %action_id, "Started active polkit auth request");
398
+                tracing::info!(
399
+                    action_id = %action_id,
400
+                    icon_name = %icon_name,
401
+                    detail_count,
402
+                    identity_count,
403
+                    "Started active polkit auth request"
404
+                );
182405
             }
183406
             QueueInsert::Queued { position } => {
184407
                 tracing::info!(
185408
                     action_id = %action_id,
409
+                    icon_name = %icon_name,
410
+                    detail_count,
411
+                    identity_count,
186412
                     queue_position = position,
187413
                     "Queued polkit auth request"
188414
                 );
@@ -404,7 +630,7 @@ mod tests {
404630
     #[test]
405631
     fn runtime_begin_authentication_updates_state_and_counts() {
406632
         let auth_state = Arc::new(AuthState::default());
407
-        let runtime = PolkitRuntime::new(Arc::clone(&auth_state));
633
+        let runtime = Arc::new(PolkitRuntime::new_without_worker(Arc::clone(&auth_state)));
408634
 
409635
         let insert = runtime
410636
             .begin_authentication(fake_request("cookie-1"))
@@ -418,7 +644,7 @@ mod tests {
418644
     #[test]
419645
     fn runtime_cancel_authentication_drops_active_request() {
420646
         let auth_state = Arc::new(AuthState::default());
421
-        let runtime = PolkitRuntime::new(Arc::clone(&auth_state));
647
+        let runtime = Arc::new(PolkitRuntime::new_without_worker(Arc::clone(&auth_state)));
422648
         runtime
423649
             .begin_authentication(fake_request("cookie-1"))
424650
             .expect("begin");
@@ -433,7 +659,7 @@ mod tests {
433659
     #[test]
434660
     fn runtime_cancel_authentication_removes_queued_request() {
435661
         let auth_state = Arc::new(AuthState::default());
436
-        let runtime = PolkitRuntime::new(Arc::clone(&auth_state));
662
+        let runtime = Arc::new(PolkitRuntime::new_without_worker(Arc::clone(&auth_state)));
437663
         runtime
438664
             .begin_authentication(fake_request("cookie-1"))
439665
             .expect("begin first");
@@ -446,4 +672,17 @@ mod tests {
446672
         assert_eq!(auth_state.summary().active_requests, 1);
447673
         assert_eq!(auth_state.summary().queued_requests, 0);
448674
     }
675
+
676
+    #[test]
677
+    fn resolve_identity_username_uses_uid_detail() {
678
+        let mut details = HashMap::new();
679
+        details.insert(
680
+            "uid".to_string(),
681
+            OwnedValue::from(nix::unistd::geteuid().as_raw()),
682
+        );
683
+        let identities = vec![("unix-user".to_string(), details)];
684
+
685
+        let resolved = resolve_identity_username(&identities);
686
+        assert_eq!(resolved, current_username());
687
+    }
449688
 }
garcard/src/daemon.rsmodified
@@ -122,21 +122,25 @@ fn init_backend(config: &Config, auth_state: Arc<AuthState>) -> Result<Box<dyn A
122122
             Ok(backend)
123123
         }
124124
         AgentBackendMode::Polkit => {
125
-            let mut backend: Box<dyn AuthAgentBackend> =
126
-                Box::new(PolkitAgent::new(PolkitBackendConfig {
125
+            let mut backend: Box<dyn AuthAgentBackend> = Box::new(PolkitAgent::new(
126
+                PolkitBackendConfig {
127127
                     object_path: config.polkit_object_path.clone(),
128128
                     locale: config.locale.clone(),
129
-                }, Arc::clone(&auth_state))?);
129
+                },
130
+                Arc::clone(&auth_state),
131
+            )?);
130132
             backend.register()?;
131133
             Ok(backend)
132134
         }
133135
         AgentBackendMode::Auto => {
134136
             let attempt = (|| -> Result<Box<dyn AuthAgentBackend>> {
135
-                let mut backend: Box<dyn AuthAgentBackend> =
136
-                    Box::new(PolkitAgent::new(PolkitBackendConfig {
137
+                let mut backend: Box<dyn AuthAgentBackend> = Box::new(PolkitAgent::new(
138
+                    PolkitBackendConfig {
137139
                         object_path: config.polkit_object_path.clone(),
138140
                         locale: config.locale.clone(),
139
-                    }, Arc::clone(&auth_state))?);
141
+                    },
142
+                    Arc::clone(&auth_state),
143
+                )?);
140144
                 backend.register()?;
141145
                 Ok(backend)
142146
             })();
garcard/src/main.rsmodified
@@ -2,6 +2,7 @@ mod agent;
22
 mod config;
33
 mod daemon;
44
 mod polkit_helper;
5
+mod prompt;
56
 mod state;
67
 
78
 use anyhow::Result;
garcard/src/polkit_helper.rsmodified
@@ -86,14 +86,20 @@ impl HelperSocketClient {
8686
 
8787
             match parse_helper_line(&line)? {
8888
                 HelperEvent::PromptHidden(prompt) => {
89
-                    match prompts.prompt_secret(&prompt).context("prompt handler failed")? {
89
+                    match prompts
90
+                        .prompt_secret(&prompt)
91
+                        .context("prompt handler failed")?
92
+                    {
9093
                         Some(response) => write_line(&mut stream, &sanitize_response(&response))
9194
                             .context("failed to send helper secret response")?,
9295
                         None => return Ok(HelperOutcome::Canceled),
9396
                     }
9497
                 }
9598
                 HelperEvent::PromptVisible(prompt) => {
96
-                    match prompts.prompt_plain(&prompt).context("prompt handler failed")? {
99
+                    match prompts
100
+                        .prompt_plain(&prompt)
101
+                        .context("prompt handler failed")?
102
+                    {
97103
                         Some(response) => write_line(&mut stream, &sanitize_response(&response))
98104
                             .context("failed to send helper visible response")?,
99105
                         None => return Ok(HelperOutcome::Canceled),
garcard/src/prompt.rsadded
@@ -0,0 +1,110 @@
1
+use crate::polkit_helper::PromptProvider;
2
+use anyhow::{Context, Result};
3
+use std::process::Command;
4
+
5
+const DEFAULT_ASK_TIMEOUT_SECS: u64 = 120;
6
+
7
+#[derive(Debug, Clone)]
8
+pub struct CommandPrompt {
9
+    prompt_command: Option<String>,
10
+}
11
+
12
+impl Default for CommandPrompt {
13
+    fn default() -> Self {
14
+        Self {
15
+            prompt_command: std::env::var("GARCARD_PROMPT_COMMAND").ok(),
16
+        }
17
+    }
18
+}
19
+
20
+impl CommandPrompt {
21
+    fn run_prompt(&self, prompt: &str, visible: bool) -> Result<Option<String>> {
22
+        if let Some(command) = self.prompt_command.as_deref() {
23
+            return run_custom_prompt_command(command, prompt, visible);
24
+        }
25
+
26
+        run_systemd_ask_password(prompt, visible)
27
+    }
28
+}
29
+
30
+impl PromptProvider for CommandPrompt {
31
+    fn prompt_secret(&mut self, prompt: &str) -> Result<Option<String>> {
32
+        self.run_prompt(prompt, false)
33
+    }
34
+
35
+    fn prompt_plain(&mut self, prompt: &str) -> Result<Option<String>> {
36
+        self.run_prompt(prompt, true)
37
+    }
38
+
39
+    fn show_error(&mut self, message: &str) -> Result<()> {
40
+        tracing::warn!("polkit helper message: {}", message);
41
+        Ok(())
42
+    }
43
+
44
+    fn show_info(&mut self, message: &str) -> Result<()> {
45
+        tracing::info!("polkit helper message: {}", message);
46
+        Ok(())
47
+    }
48
+}
49
+
50
+fn run_custom_prompt_command(command: &str, prompt: &str, visible: bool) -> Result<Option<String>> {
51
+    let mode = if visible { "plain" } else { "secret" };
52
+    let output = Command::new("sh")
53
+        .arg("-c")
54
+        .arg(command)
55
+        .env("GARCARD_PROMPT", prompt)
56
+        .env("GARCARD_PROMPT_MODE", mode)
57
+        .output()
58
+        .with_context(|| format!("failed to run custom prompt command: {}", command))?;
59
+
60
+    if !output.status.success() {
61
+        return Ok(None);
62
+    }
63
+
64
+    Ok(extract_response(&output.stdout))
65
+}
66
+
67
+fn run_systemd_ask_password(prompt: &str, visible: bool) -> Result<Option<String>> {
68
+    let mut command = Command::new("systemd-ask-password");
69
+    command.arg("--user");
70
+    command.arg("--no-tty");
71
+    command.arg(format!("--timeout={}", DEFAULT_ASK_TIMEOUT_SECS));
72
+    if visible {
73
+        command.arg("--echo=yes");
74
+    }
75
+    command.arg(prompt);
76
+
77
+    let output = command
78
+        .output()
79
+        .context("failed to run systemd-ask-password")?;
80
+    if !output.status.success() {
81
+        return Ok(None);
82
+    }
83
+
84
+    Ok(extract_response(&output.stdout))
85
+}
86
+
87
+fn extract_response(stdout: &[u8]) -> Option<String> {
88
+    let response = String::from_utf8_lossy(stdout).trim().to_string();
89
+    if response.is_empty() {
90
+        None
91
+    } else {
92
+        Some(response)
93
+    }
94
+}
95
+
96
+#[cfg(test)]
97
+mod tests {
98
+    use super::*;
99
+
100
+    #[test]
101
+    fn extract_response_trims_whitespace() {
102
+        let response = extract_response(b"  hello world \n");
103
+        assert_eq!(response.as_deref(), Some("hello world"));
104
+    }
105
+
106
+    #[test]
107
+    fn extract_response_rejects_empty_value() {
108
+        assert_eq!(extract_response(b" \n\t"), None);
109
+    }
110
+}
garcard/src/state.rsmodified
@@ -7,6 +7,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
77
 use std::time::Instant;
88
 
99
 /// Typed phases for auth flow transitions.
10
+#[allow(dead_code)]
1011
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1112
 pub enum AuthPhase {
1213
     Idle,
@@ -30,21 +31,6 @@ impl AuthPhase {
3031
             Self::Timeout => "timeout",
3132
         }
3233
     }
33
-
34
-    pub fn from_label(raw: &str) -> Option<Self> {
35
-        match raw.trim().to_ascii_lowercase().as_str() {
36
-            "idle" => Some(Self::Idle),
37
-            "pending" | "pending_prompt" | "pending-prompt" | "pending prompt" => {
38
-                Some(Self::PendingPrompt)
39
-            }
40
-            "verifying" => Some(Self::Verifying),
41
-            "success" => Some(Self::Success),
42
-            "failure" | "failed" => Some(Self::Failure),
43
-            "canceled" | "cancelled" => Some(Self::Canceled),
44
-            "timeout" | "timed_out" => Some(Self::Timeout),
45
-            _ => None,
46
-        }
47
-    }
4834
 }
4935
 
5036
 impl fmt::Display for AuthPhase {
@@ -94,7 +80,10 @@ impl Default for AuthState {
9480
 
9581
 impl AuthState {
9682
     pub fn phase(&self) -> AuthPhase {
97
-        self.current_phase.read().map(|phase| *phase).unwrap_or(AuthPhase::Idle)
83
+        self.current_phase
84
+            .read()
85
+            .map(|phase| *phase)
86
+            .unwrap_or(AuthPhase::Idle)
9887
     }
9988
 
10089
     pub fn set_phase(&self, next: AuthPhase) {
@@ -115,12 +104,6 @@ impl AuthState {
115104
         false
116105
     }
117106
 
118
-    pub fn set_state(&self, next: impl AsRef<str>) {
119
-        if let Some(phase) = AuthPhase::from_label(next.as_ref()) {
120
-            self.set_phase(phase);
121
-        }
122
-    }
123
-
124107
     pub fn set_active_requests(&self, count: usize) {
125108
         self.active_requests.store(count, Ordering::Relaxed);
126109
     }
@@ -182,11 +165,18 @@ impl<T> AuthQueue<T> {
182165
         self.active.as_ref()
183166
     }
184167
 
185
-    pub fn active_mut(&mut self) -> Option<&mut T> {
186
-        self.active.as_mut()
168
+    pub fn take_active_if<F>(&mut self, mut predicate: F) -> Option<T>
169
+    where
170
+        F: FnMut(&T) -> bool,
171
+    {
172
+        if self.active.as_ref().is_some_and(&mut predicate) {
173
+            return self.complete_active();
174
+        }
175
+
176
+        None
187177
     }
188178
 
189
-    pub fn take_active_if<F>(&mut self, mut predicate: F) -> Option<T>
179
+    pub fn complete_active_if<F>(&mut self, mut predicate: F) -> Option<T>
190180
     where
191181
         F: FnMut(&T) -> bool,
192182
     {
@@ -215,10 +205,6 @@ impl<T> AuthQueue<T> {
215205
         finished
216206
     }
217207
 
218
-    pub fn cancel_active(&mut self) -> Option<T> {
219
-        self.complete_active()
220
-    }
221
-
222208
     pub fn active_len(&self) -> usize {
223209
         usize::from(self.active.is_some())
224210
     }
@@ -262,7 +248,11 @@ impl RuntimeState {
262248
         Self::with_auth(socket_path, backend_name, Arc::new(AuthState::default()))
263249
     }
264250
 
265
-    pub fn with_auth(socket_path: String, backend_name: &'static str, auth: Arc<AuthState>) -> Self {
251
+    pub fn with_auth(
252
+        socket_path: String,
253
+        backend_name: &'static str,
254
+        auth: Arc<AuthState>,
255
+    ) -> Self {
266256
         auth.set_phase(AuthPhase::Idle);
267257
         auth.set_active_requests(0);
268258
         auth.set_queued_requests(0);