Skip to main content

openvpn_mgmt_codec/
auth.rs

1use std::str::FromStr;
2
3/// Error returned when a string is not a recognized auth type.
4#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
5#[error("unrecognized auth type: {0:?}")]
6pub struct ParseAuthTypeError(pub String);
7
8/// Error returned when a string is not a recognized auth retry mode.
9#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
10#[error("unrecognized auth retry mode: {0:?}")]
11pub struct ParseAuthRetryModeError(pub String);
12
13/// Authentication credential type. OpenVPN identifies credential requests
14/// by a quoted type string — usually `"Auth"` or `"Private Key"`, but
15/// plugins can define custom types.
16#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
17pub enum AuthType {
18    /// Standard `--auth-user-pass` credentials. Wire: `"Auth"`.
19    Auth,
20
21    /// Private key passphrase (encrypted key file). Wire: `"Private Key"`.
22    #[strum(to_string = "Private Key")]
23    PrivateKey,
24
25    /// HTTP proxy credentials. Wire: `"HTTP Proxy"`.
26    #[strum(to_string = "HTTP Proxy")]
27    HttpProxy,
28
29    /// SOCKS proxy credentials. Wire: `"SOCKS Proxy"`.
30    #[strum(to_string = "SOCKS Proxy")]
31    SocksProxy,
32
33    /// Plugin-defined or otherwise unrecognized auth type.
34    #[strum(default)]
35    Unknown(String),
36}
37
38impl FromStr for AuthType {
39    type Err = ParseAuthTypeError;
40
41    /// Parse a recognized auth type string.
42    ///
43    /// Recognized values: `Auth`, `Private Key`, `HTTP Proxy`, `SOCKS Proxy`.
44    /// Returns `Err` for anything else — use [`AuthType::Unknown`] explicitly
45    /// if forward-compatible fallback is desired.
46    fn from_str(input: &str) -> Result<Self, Self::Err> {
47        match input {
48            "Auth" => Ok(Self::Auth),
49            "Private Key" => Ok(Self::PrivateKey),
50            "HTTP Proxy" => Ok(Self::HttpProxy),
51            "SOCKS Proxy" => Ok(Self::SocksProxy),
52            other => Err(ParseAuthTypeError(other.to_string())),
53        }
54    }
55}
56
57/// Controls how OpenVPN retries after authentication failure.
58#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
59#[strum(serialize_all = "lowercase")]
60pub enum AuthRetryMode {
61    /// Don't retry — exit on auth failure.
62    None,
63
64    /// Retry, re-prompting for credentials.
65    Interact,
66
67    /// Retry without re-prompting.
68    #[strum(to_string = "nointeract")]
69    NoInteract,
70}
71
72impl FromStr for AuthRetryMode {
73    type Err = ParseAuthRetryModeError;
74
75    /// Parse an auth-retry mode: `none`, `interact`, or `nointeract`.
76    fn from_str(input: &str) -> Result<Self, Self::Err> {
77        match input {
78            "none" => Ok(Self::None),
79            "interact" => Ok(Self::Interact),
80            "nointeract" => Ok(Self::NoInteract),
81            other => Err(ParseAuthRetryModeError(other.to_string())),
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use test_case::test_case;
90
91    #[test_case(AuthType::Auth)]
92    #[test_case(AuthType::PrivateKey)]
93    #[test_case(AuthType::HttpProxy)]
94    #[test_case(AuthType::SocksProxy)]
95    fn auth_type_roundtrip(at: AuthType) {
96        let string = at.to_string();
97        assert_eq!(string.parse::<AuthType>().unwrap(), at);
98    }
99
100    #[test]
101    fn auth_type_phantom_aliases_are_rejected() {
102        assert!("PrivateKey".parse::<AuthType>().is_err());
103        assert!("HTTPProxy".parse::<AuthType>().is_err());
104        assert!("SOCKSProxy".parse::<AuthType>().is_err());
105    }
106
107    #[test]
108    fn auth_type_unknown_is_err() {
109        assert!("MyPlugin".parse::<AuthType>().is_err());
110    }
111
112    #[test_case(AuthRetryMode::None)]
113    #[test_case(AuthRetryMode::Interact)]
114    #[test_case(AuthRetryMode::NoInteract)]
115    fn auth_retry_roundtrip(mode: AuthRetryMode) {
116        let string = mode.to_string();
117        assert_eq!(string.parse::<AuthRetryMode>().unwrap(), mode);
118    }
119
120    #[test]
121    fn auth_retry_invalid() {
122        assert!("bogus".parse::<AuthRetryMode>().is_err());
123    }
124}