distant_auth/
msg.rs

1use std::collections::HashMap;
2
3use derive_more::{Display, Error, From};
4use serde::{Deserialize, Serialize};
5
6/// Represents messages from an authenticator that act as initiators such as providing
7/// a challenge, verifying information, presenting information, or highlighting an error
8#[derive(Clone, Debug, From, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case", tag = "type")]
10pub enum Authentication {
11    /// Indicates the beginning of authentication, providing available methods
12    #[serde(rename = "auth_initialization")]
13    Initialization(Initialization),
14
15    /// Indicates that authentication is starting for the specific `method`
16    #[serde(rename = "auth_start_method")]
17    StartMethod(StartMethod),
18
19    /// Issues a challenge to be answered
20    #[serde(rename = "auth_challenge")]
21    Challenge(Challenge),
22
23    /// Requests verification of some text
24    #[serde(rename = "auth_verification")]
25    Verification(Verification),
26
27    /// Reports some information associated with authentication
28    #[serde(rename = "auth_info")]
29    Info(Info),
30
31    /// Reports an error occurrred during authentication
32    #[serde(rename = "auth_error")]
33    Error(Error),
34
35    /// Indicates that the authentication of all methods is finished
36    #[serde(rename = "auth_finished")]
37    Finished,
38}
39
40/// Represents the beginning of the authentication procedure
41#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
42pub struct Initialization {
43    /// Available methods to use for authentication
44    pub methods: Vec<String>,
45}
46
47/// Represents the start of authentication for some method
48#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub struct StartMethod {
50    pub method: String,
51}
52
53/// Represents a challenge comprising a series of questions to be presented
54#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
55pub struct Challenge {
56    pub questions: Vec<Question>,
57    pub options: HashMap<String, String>,
58}
59
60/// Represents an ask to verify some information
61#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62pub struct Verification {
63    pub kind: VerificationKind,
64    pub text: String,
65}
66
67/// Represents some information to be presented related to authentication
68#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
69pub struct Info {
70    pub text: String,
71}
72
73/// Represents authentication messages that are responses to authenticator requests such
74/// as answers to challenges or verifying information
75#[derive(Clone, Debug, From, PartialEq, Eq, Serialize, Deserialize)]
76#[serde(rename_all = "snake_case", tag = "type")]
77pub enum AuthenticationResponse {
78    /// Contains response to initialization, providing details about which methods to use
79    #[serde(rename = "auth_initialization_response")]
80    Initialization(InitializationResponse),
81
82    /// Contains answers to challenge request
83    #[serde(rename = "auth_challenge_response")]
84    Challenge(ChallengeResponse),
85
86    /// Contains response to a verification request
87    #[serde(rename = "auth_verification_response")]
88    Verification(VerificationResponse),
89}
90
91/// Represents a response to initialization to specify which authentication methods to pursue
92#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
93pub struct InitializationResponse {
94    /// Methods to use (in order as provided)
95    pub methods: Vec<String>,
96}
97
98/// Represents the answers to a previously-asked challenge associated with authentication
99#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
100pub struct ChallengeResponse {
101    /// Answers to challenge questions (in order relative to questions)
102    pub answers: Vec<String>,
103}
104
105/// Represents the answer to a previously-asked verification associated with authentication
106#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
107pub struct VerificationResponse {
108    /// Whether or not the verification was deemed valid
109    pub valid: bool,
110}
111
112/// Represents the type of verification being requested
113#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "snake_case")]
115pub enum VerificationKind {
116    /// An ask to verify the host such as with SSH
117    #[display(fmt = "host")]
118    Host,
119
120    /// When the verification is unknown (happens when other side is unaware of the kind)
121    #[display(fmt = "unknown")]
122    #[serde(other)]
123    Unknown,
124}
125
126impl VerificationKind {
127    /// Returns all variants except "unknown"
128    pub const fn known_variants() -> &'static [Self] {
129        &[Self::Host]
130    }
131}
132
133/// Represents a single question in a challenge associated with authentication
134#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
135pub struct Question {
136    /// Label associated with the question for more programmatic usage
137    pub label: String,
138
139    /// The text of the question (used for display purposes)
140    pub text: String,
141
142    /// Any options information specific to a particular auth domain
143    /// such as including a username and instructions for SSH authentication
144    pub options: HashMap<String, String>,
145}
146
147impl Question {
148    /// Creates a new question without any options data using `text` for both label and text
149    pub fn new(text: impl Into<String>) -> Self {
150        let text = text.into();
151
152        Self {
153            label: text.clone(),
154            text,
155            options: HashMap::new(),
156        }
157    }
158}
159
160/// Represents some error that occurred during authentication
161#[derive(Clone, Debug, Display, Error, PartialEq, Eq, Serialize, Deserialize)]
162#[display(fmt = "{kind}: {text}")]
163pub struct Error {
164    /// Represents the kind of error
165    pub kind: ErrorKind,
166
167    /// Description of the error
168    pub text: String,
169}
170
171impl Error {
172    /// Creates a fatal error
173    pub fn fatal(text: impl Into<String>) -> Self {
174        Self {
175            kind: ErrorKind::Fatal,
176            text: text.into(),
177        }
178    }
179
180    /// Creates a non-fatal error
181    pub fn non_fatal(text: impl Into<String>) -> Self {
182        Self {
183            kind: ErrorKind::Error,
184            text: text.into(),
185        }
186    }
187
188    /// Returns true if error represents a fatal error, meaning that there is no recovery possible
189    /// from this error
190    pub fn is_fatal(&self) -> bool {
191        self.kind.is_fatal()
192    }
193
194    /// Converts the error into a [`std::io::Error`] representing permission denied
195    pub fn into_io_permission_denied(self) -> std::io::Error {
196        std::io::Error::new(std::io::ErrorKind::PermissionDenied, self)
197    }
198}
199
200/// Represents the type of error encountered during authentication
201#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, Serialize, Deserialize)]
202#[serde(rename_all = "snake_case")]
203pub enum ErrorKind {
204    /// Error is unrecoverable
205    Fatal,
206
207    /// Error is recoverable
208    Error,
209}
210
211impl ErrorKind {
212    /// Returns true if error kind represents a fatal error, meaning that there is no recovery
213    /// possible from this error
214    pub fn is_fatal(self) -> bool {
215        matches!(self, Self::Fatal)
216    }
217}