Skip to main content

auths_cli/core/
provider.rs

1use anyhow::Context;
2use auths_core::error::AgentError;
3use auths_core::signing::PassphraseProvider;
4use zeroize::Zeroizing;
5
6/// A PassphraseProvider implementation that prompts the user on the command line.
7#[derive(Debug, Clone, Default)]
8pub struct CliPassphraseProvider;
9
10impl CliPassphraseProvider {
11    /// Creates a new instance.
12    pub fn new() -> Self {
13        Self
14    }
15}
16
17impl PassphraseProvider for CliPassphraseProvider {
18    /// Securely obtains a passphrase by prompting the user on the terminal.
19    fn get_passphrase(&self, prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
20        // Print the contextual prompt provided by the caller to stderr
21        eprintln!("{}", prompt_message);
22
23        // Use rpassword to prompt securely
24        let password = rpassword::prompt_password("Enter passphrase: ")
25            .context("Failed to read passphrase from terminal") // Add context using anyhow
26            .map_err(|e| {
27                // Map the anyhow::Error (wrapping std::io::Error) to AgentError
28                // Consider adding a specific AgentError variant like UserInputCancelled or IOFailed
29                eprintln!("Error reading passphrase: {:?}", e); // Log the specific error
30                // For now, map generic IO errors. A more specific mapping could be done.
31                if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
32                    // Example: Check for specific kinds like Interrupted if needed
33                    // if io_err.kind() == std::io::ErrorKind::Interrupted {
34                    //     return AgentError::UserInputCancelled;
35                    // }
36                    AgentError::IO(std::io::Error::new(io_err.kind(), format!("{}", e))) // Create a new IO error to own the message
37                } else {
38                    AgentError::SecurityError(format!("Failed to get passphrase: {}", e)) // Fallback
39                }
40            })?;
41
42        Ok(Zeroizing::new(password))
43    }
44}
45
46/// A PassphraseProvider that returns a pre-collected passphrase.
47///
48/// Use this when the passphrase must be collected before starting a
49/// background task (e.g., a terminal spinner) that would interfere
50/// with stdin.
51pub struct PrefilledPassphraseProvider {
52    passphrase: Zeroizing<String>,
53}
54
55impl PrefilledPassphraseProvider {
56    pub fn new(passphrase: Zeroizing<String>) -> Self {
57        Self { passphrase }
58    }
59}
60
61impl PassphraseProvider for PrefilledPassphraseProvider {
62    fn get_passphrase(&self, _prompt_message: &str) -> Result<Zeroizing<String>, AgentError> {
63        Ok(self.passphrase.clone())
64    }
65}