1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use std::convert::TryFrom;
use crate::ChallengeRef;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BasicClient {
realm: Box<str>,
}
impl BasicClient {
pub fn realm(&self) -> &str {
&*self.realm
}
pub fn respond(&self, username: &str, password: &str) -> String {
let user_pass = format!("{}:{}", username, password);
const PREFIX: &str = "Basic ";
let mut value = String::with_capacity(PREFIX.len() + base64_encoded_len(user_pass.len()));
value.push_str(PREFIX);
base64::encode_config_buf(&user_pass[..], base64::STANDARD, &mut value);
value
}
}
impl TryFrom<&ChallengeRef<'_>> for BasicClient {
type Error = String;
fn try_from(value: &ChallengeRef<'_>) -> Result<Self, Self::Error> {
if !value.scheme.eq_ignore_ascii_case("Basic") {
return Err(format!(
"BasicClient doesn't support challenge scheme {:?}",
value.scheme
));
}
let mut realm = None;
for (k, v) in &value.params {
if k.eq_ignore_ascii_case("realm") {
realm = Some(v.to_unescaped());
}
}
let realm = realm.ok_or("missing required parameter realm")?;
Ok(BasicClient {
realm: realm.into_boxed_str(),
})
}
}
fn base64_encoded_len(input_len: usize) -> usize {
(input_len + 2) / 3 * 4
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
let ctx = BasicClient {
realm: "WallyWorld".into(),
};
assert_eq!(
ctx.respond("Aladdin", "open sesame"),
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
let ctx = BasicClient {
realm: "foo".into(),
};
assert_eq!(ctx.respond("test", "123\u{A3}"), "Basic dGVzdDoxMjPCow==");
}
}