distant_auth/handler/methods/
prompt.rs

1use std::io;
2
3use async_trait::async_trait;
4use log::*;
5
6use crate::handler::AuthMethodHandler;
7use crate::msg::{
8    Challenge, ChallengeResponse, Error, Info, Verification, VerificationKind, VerificationResponse,
9};
10
11/// Blocking implementation of [`AuthMethodHandler`] that uses prompts to communicate challenge &
12/// verification requests, receiving responses to relay back.
13pub struct PromptAuthMethodHandler<T, U> {
14    text_prompt: T,
15    password_prompt: U,
16}
17
18impl<T, U> PromptAuthMethodHandler<T, U> {
19    pub fn new(text_prompt: T, password_prompt: U) -> Self {
20        Self {
21            text_prompt,
22            password_prompt,
23        }
24    }
25}
26
27#[async_trait]
28impl<T, U> AuthMethodHandler for PromptAuthMethodHandler<T, U>
29where
30    T: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
31    U: Fn(&str) -> io::Result<String> + Send + Sync + 'static,
32{
33    async fn on_challenge(&mut self, challenge: Challenge) -> io::Result<ChallengeResponse> {
34        trace!("on_challenge({challenge:?})");
35        let mut answers = Vec::new();
36        for question in challenge.questions.iter() {
37            // Contains all prompt lines including same line
38            let mut lines = question.text.split('\n').collect::<Vec<_>>();
39
40            // Line that is prompt on same line as answer
41            let line = lines.pop().unwrap();
42
43            // Go ahead and display all other lines
44            for line in lines.into_iter() {
45                eprintln!("{line}");
46            }
47
48            // Get an answer from user input, or use a blank string as an answer
49            // if we fail to get input from the user
50            let answer = (self.password_prompt)(line).unwrap_or_default();
51
52            answers.push(answer);
53        }
54        Ok(ChallengeResponse { answers })
55    }
56
57    async fn on_verification(
58        &mut self,
59        verification: Verification,
60    ) -> io::Result<VerificationResponse> {
61        trace!("on_verify({verification:?})");
62        match verification.kind {
63            VerificationKind::Host => {
64                eprintln!("{}", verification.text);
65
66                let answer = (self.text_prompt)("Enter [y/N]> ")?;
67                trace!("Verify? Answer = '{answer}'");
68                Ok(VerificationResponse {
69                    valid: matches!(answer.trim(), "y" | "Y" | "yes" | "YES"),
70                })
71            }
72            x => {
73                error!("Unsupported verify kind: {x}");
74                Ok(VerificationResponse { valid: false })
75            }
76        }
77    }
78
79    async fn on_info(&mut self, info: Info) -> io::Result<()> {
80        trace!("on_info({info:?})");
81        println!("{}", info.text);
82        Ok(())
83    }
84
85    async fn on_error(&mut self, error: Error) -> io::Result<()> {
86        trace!("on_error({error:?})");
87        eprintln!("{}: {}", error.kind, error.text);
88        Ok(())
89    }
90}