Skip to main content

openvpn_mgmt_codec/
auth.rs

1use std::fmt;
2use std::str::FromStr;
3
4/// Authentication credential type. OpenVPN identifies credential requests
5/// by a quoted type string — usually `"Auth"` or `"Private Key"`, but
6/// plugins can define custom types.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum AuthType {
9    /// Standard `--auth-user-pass` credentials. Wire: `"Auth"`.
10    Auth,
11
12    /// Private key passphrase (encrypted key file). Wire: `"Private Key"`.
13    PrivateKey,
14
15    /// HTTP proxy credentials. Wire: `"HTTP Proxy"`.
16    HttpProxy,
17
18    /// SOCKS proxy credentials. Wire: `"SOCKS Proxy"`.
19    SocksProxy,
20
21    /// Plugin-defined or otherwise unrecognized auth type.
22    Custom(String),
23}
24
25impl fmt::Display for AuthType {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Self::Auth => f.write_str("Auth"),
29            Self::PrivateKey => f.write_str("Private Key"),
30            Self::HttpProxy => f.write_str("HTTP Proxy"),
31            Self::SocksProxy => f.write_str("SOCKS Proxy"),
32            Self::Custom(s) => f.write_str(s),
33        }
34    }
35}
36
37impl FromStr for AuthType {
38    type Err = std::convert::Infallible;
39
40    /// Parse an auth type string. Recognized values: `Auth`, `PrivateKey` /
41    /// `Private Key`, `HTTPProxy` / `HTTP Proxy`, `SOCKSProxy` / `SOCKS Proxy`.
42    /// Anything else becomes [`AuthType::Custom`].
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        Ok(match s {
45            "Auth" => Self::Auth,
46            "PrivateKey" | "Private Key" => Self::PrivateKey,
47            "HTTPProxy" | "HTTP Proxy" => Self::HttpProxy,
48            "SOCKSProxy" | "SOCKS Proxy" => Self::SocksProxy,
49            other => Self::Custom(other.to_string()),
50        })
51    }
52}
53
54/// Controls how OpenVPN retries after authentication failure.
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum AuthRetryMode {
57    /// Don't retry — exit on auth failure.
58    None,
59
60    /// Retry, re-prompting for credentials.
61    Interact,
62
63    /// Retry without re-prompting.
64    NoInteract,
65}
66
67impl fmt::Display for AuthRetryMode {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            Self::None => f.write_str("none"),
71            Self::Interact => f.write_str("interact"),
72            Self::NoInteract => f.write_str("nointeract"),
73        }
74    }
75}
76
77impl FromStr for AuthRetryMode {
78    type Err = String;
79
80    /// Parse an auth-retry mode: `none`, `interact`, or `nointeract`.
81    fn from_str(s: &str) -> Result<Self, Self::Err> {
82        match s {
83            "none" => Ok(Self::None),
84            "interact" => Ok(Self::Interact),
85            "nointeract" => Ok(Self::NoInteract),
86            _ => Err(format!(
87                "invalid auth-retry mode: {s} (use none/interact/nointeract)"
88            )),
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn auth_type_roundtrip() {
99        for at in [
100            AuthType::Auth,
101            AuthType::PrivateKey,
102            AuthType::HttpProxy,
103            AuthType::SocksProxy,
104        ] {
105            let s = at.to_string();
106            assert_eq!(s.parse::<AuthType>().unwrap(), at);
107        }
108    }
109
110    #[test]
111    fn auth_type_aliases() {
112        assert_eq!(
113            "PrivateKey".parse::<AuthType>().unwrap(),
114            AuthType::PrivateKey
115        );
116        assert_eq!(
117            "Private Key".parse::<AuthType>().unwrap(),
118            AuthType::PrivateKey
119        );
120        assert_eq!(
121            "HTTPProxy".parse::<AuthType>().unwrap(),
122            AuthType::HttpProxy
123        );
124        assert_eq!(
125            "SOCKSProxy".parse::<AuthType>().unwrap(),
126            AuthType::SocksProxy
127        );
128    }
129
130    #[test]
131    fn auth_type_custom_fallback() {
132        assert_eq!(
133            "MyPlugin".parse::<AuthType>().unwrap(),
134            AuthType::Custom("MyPlugin".to_string())
135        );
136    }
137
138    #[test]
139    fn auth_retry_roundtrip() {
140        for mode in [
141            AuthRetryMode::None,
142            AuthRetryMode::Interact,
143            AuthRetryMode::NoInteract,
144        ] {
145            let s = mode.to_string();
146            assert_eq!(s.parse::<AuthRetryMode>().unwrap(), mode);
147        }
148    }
149
150    #[test]
151    fn auth_retry_invalid() {
152        assert!("bogus".parse::<AuthRetryMode>().is_err());
153    }
154}