http_basic_auth/
lib.rs

1mod error;
2
3use error::ParseError;
4
5#[derive(Debug, PartialEq)]
6pub struct Credential {
7    pub user_id: String,
8    pub password: String
9}
10
11pub fn decode(s: &str) -> Result<Credential, ParseError> {
12    match s {
13	s if s.starts_with("Basic ") => {
14	    let decoded = base64::decode(&s[6..])?;
15	    let decoded = std::str::from_utf8(&decoded)?;
16	    let parts: Vec<&str> = decoded.splitn(2, ":").collect();
17	    match &parts.as_slice() {
18		[user, pass] => Ok(Credential { user_id: user.to_string(), password: pass.to_string() }),
19		_ => Err(ParseError::Format)
20	    }
21	}
22	_ => Err(ParseError::Scheme)
23    }
24}
25
26impl std::str::FromStr for Credential {
27    type Err = ParseError;
28    fn from_str(s: &str) -> Result<Self, Self::Err> {
29	decode(s)
30    }
31}
32
33#[cfg(test)]
34mod test {
35    use super::*;
36
37    fn encode(c: &Credential) -> String {
38	let unencoded = format!("{}:{}", c.user_id, c.password);
39	let encoded = base64::encode(unencoded);
40	format!("Basic {}", encoded)
41    }
42
43    fn encode_str(s: &str) -> String {
44	let encoded = base64::encode(s);
45	format!("Basic {}", encoded)
46    }
47
48    #[test]
49    fn identify_scheme() {
50	let c = Credential {
51	    user_id: "Aladdin".to_string(),
52	    password: "open sesame".to_string()
53	};
54	let s = encode(&c);
55	let c2 = decode(&s);
56	assert_eq!(Ok(c), c2);
57    }
58
59    #[test]
60    fn no_colon() {
61	let s = encode_str("john");
62	let c = decode(&s);
63	assert_eq!(Err(ParseError::Format), c);
64    }
65
66    #[test]
67    fn allow_empty_password() {
68	let s = encode_str("john:");
69	let c = decode(&s);
70	assert_ne!(Err(ParseError::Format), c);
71    }
72
73    #[test]
74    fn bad_base64() {
75	let s = "Basic abcdefg";
76	let c = decode(&s);
77	assert_eq!(Err(ParseError::Decode), c);
78    }
79
80    #[test]
81    fn bad_scheme() {
82	let c = Credential {
83	    user_id: "john".to_string(),
84	    password: "hunter2".to_string(),
85	};
86	let unencoded = format!("{}:{}", c.user_id, c.password);
87	let encoded = base64::encode(unencoded);
88	let formatted = format!("Bearer {}", encoded);
89	let c = decode(&formatted);
90	assert_eq!(Err(ParseError::Scheme), c);
91    }
92}