1use crate::config::setopt::{EasyHandle, SetOpt, SetOptError, SetOptProxy};
4use std::{
5 fmt,
6 ops::{BitOr, BitOrAssign},
7};
8
9#[cfg(all(windows, feature = "spnego", not(feature = "native-tls")))]
10compile_error!("SPNEGO requires native-tls on Windows");
11
12#[derive(Clone)]
15pub struct Credentials {
16 username: String,
17 password: String,
18}
19
20impl Credentials {
21 pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
23 Self {
24 username: username.into(),
25 password: password.into(),
26 }
27 }
28}
29
30impl SetOpt for Credentials {
31 fn set_opt(&self, easy: &mut EasyHandle) -> Result<(), SetOptError> {
32 easy.username(&self.username)?;
33 easy.password(&self.password)?;
34 Ok(())
35 }
36}
37
38impl SetOptProxy for Credentials {
39 fn set_opt_proxy(&self, easy: &mut EasyHandle) -> Result<(), SetOptError> {
40 easy.proxy_username(&self.username)?;
41 easy.proxy_password(&self.password)?;
42 Ok(())
43 }
44}
45
46impl fmt::Debug for Credentials {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("Credentials")
51 .field("username", &self.username)
52 .field("password", &"*****")
53 .finish()
54 }
55}
56
57#[derive(Clone, Debug)]
59pub struct Authentication(u8);
60
61impl Default for Authentication {
62 fn default() -> Self {
63 Self::none()
64 }
65}
66
67impl Authentication {
68 pub const fn none() -> Self {
70 Authentication(0)
71 }
72
73 pub const fn all() -> Self {
75 #[allow(unused_mut)]
76 let mut all = Self::basic().0 | Self::digest().0;
77
78 #[cfg(feature = "spnego")]
79 {
80 all |= Self::negotiate().0;
81 }
82
83 Authentication(all)
84 }
85
86 pub const fn basic() -> Self {
92 Authentication(0b0001)
93 }
94
95 pub const fn digest() -> Self {
101 Authentication(0b0010)
102 }
103
104 #[cfg(feature = "spnego")]
120 pub const fn negotiate() -> Self {
121 Authentication(0b0100)
122 }
123
124 const fn contains(&self, other: Self) -> bool {
125 (self.0 & other.0) == other.0
126 }
127
128 fn as_auth(&self) -> curl::easy::Auth {
129 let mut auth = curl::easy::Auth::new();
130
131 if self.contains(Authentication::basic()) {
132 auth.basic(true);
133 }
134
135 if self.contains(Authentication::digest()) {
136 auth.digest(true);
137 }
138
139 #[cfg(feature = "spnego")]
140 {
141 if self.contains(Authentication::negotiate()) {
142 auth.gssnegotiate(true);
143 }
144 }
145
146 auth
147 }
148}
149
150impl BitOr for Authentication {
151 type Output = Self;
152
153 fn bitor(mut self, other: Self) -> Self {
154 self |= other;
155 self
156 }
157}
158
159impl BitOrAssign for Authentication {
160 fn bitor_assign(&mut self, rhs: Self) {
161 self.0 |= rhs.0;
162 }
163}
164
165impl SetOpt for Authentication {
166 fn set_opt(&self, easy: &mut EasyHandle) -> Result<(), SetOptError> {
167 #[cfg(feature = "spnego")]
168 {
169 if self.contains(Authentication::negotiate()) {
170 easy.username("")?;
173 easy.password("")?;
174 }
175 }
176
177 easy.http_auth(&self.as_auth())?;
178
179 Ok(())
180 }
181}
182
183impl SetOptProxy for Authentication {
184 fn set_opt_proxy(&self, easy: &mut EasyHandle) -> Result<(), SetOptError> {
185 #[cfg(feature = "spnego")]
186 {
187 if self.contains(Authentication::negotiate()) {
188 easy.proxy_username("")?;
191 easy.proxy_password("")?;
192 }
193 }
194
195 easy.proxy_auth(&self.as_auth())?;
196
197 Ok(())
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::Authentication;
204
205 #[test]
206 fn auth_default() {
207 let auth = Authentication::default();
208
209 assert!(!auth.contains(Authentication::basic()));
210 assert!(!auth.contains(Authentication::digest()));
211 }
212
213 #[test]
214 fn auth_all() {
215 let auth = Authentication::all();
216
217 assert!(auth.contains(Authentication::basic()));
218 assert!(auth.contains(Authentication::digest()));
219 }
220
221 #[test]
222 fn auth_single() {
223 let auth = Authentication::basic();
224
225 assert!(auth.contains(Authentication::basic()));
226 assert!(!auth.contains(Authentication::digest()));
227
228 let auth = Authentication::digest();
229
230 assert!(!auth.contains(Authentication::basic()));
231 assert!(auth.contains(Authentication::digest()));
232 }
233}