rouille/input/basic_http_auth.rs
1// Copyright (c) 2016 The Rouille developers
2// Licensed under the Apache License, Version 2.0
3// <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! Analyze the request's headers and body.
11//!
12//! This module provides functions and sub-modules that allow you to easily analyze or parse the
13//! request's headers and body.
14//!
15//! - In order to parse JSON, see [the `json` module](json/input.html).
16//! - In order to parse input from HTML forms, see [the `post` module](post/input.html).
17//! - In order to read a plain text body, see
18//! [the `plain_text_body` function](fn.plain_text_body.html).
19
20use base64;
21use Request;
22
23/// Credentials returned by `basic_http_auth`.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct HttpAuthCredentials {
26 /// Login provided by the client.
27 pub login: String,
28 /// Password provided by the client.
29 pub password: String,
30}
31
32/// Attempts to parse a `Authorization` header with basic HTTP auth.
33///
34/// If such a header is present and valid, a `HttpAuthCredentials` is returned.
35///
36/// # Example
37///
38/// ```
39/// use rouille::input;
40/// use rouille::Request;
41/// use rouille::Response;
42///
43/// fn handle(request: &Request) -> Response {
44/// let auth = match input::basic_http_auth(request) {
45/// Some(a) => a,
46/// None => return Response::basic_http_auth_login_required("realm")
47/// };
48///
49/// if auth.login == "admin" && auth.password == "GT5GeKyLvKLxuc7mjF5h" {
50/// handle_after_login(request)
51/// } else {
52/// Response::text("Bad login/password").with_status_code(403)
53/// }
54/// }
55///
56/// fn handle_after_login(request: &Request) -> Response {
57/// Response::text("You are in a secret area")
58/// }
59/// ```
60pub fn basic_http_auth(request: &Request) -> Option<HttpAuthCredentials> {
61 let header = match request.header("Authorization") {
62 None => return None,
63 Some(h) => h,
64 };
65
66 let mut split = header.splitn(2, |c| c == ' ');
67 let authtype = match split.next() {
68 None => return None,
69 Some(t) => t,
70 };
71
72 if authtype != "Basic" {
73 return None;
74 }
75
76 let authvalue = match split.next().and_then(|val| base64::decode(val).ok()) {
77 Some(v) => v,
78 None => return None,
79 };
80
81 let mut split = authvalue.splitn(2, |&c| c == b':');
82
83 let login = match split
84 .next()
85 .map(Vec::from)
86 .and_then(|l| String::from_utf8(l).ok())
87 {
88 Some(l) => l,
89 None => return None,
90 };
91 let password = match split
92 .next()
93 .map(Vec::from)
94 .and_then(|p| String::from_utf8(p).ok())
95 {
96 Some(p) => p,
97 None => return None,
98 };
99
100 Some(HttpAuthCredentials { login, password })
101}
102
103#[cfg(test)]
104mod test {
105 use super::basic_http_auth;
106 use super::HttpAuthCredentials;
107 use Request;
108
109 #[test]
110 fn basic_http_auth_no_header() {
111 let request = Request::fake_http("GET", "/", vec![], Vec::new());
112 assert_eq!(basic_http_auth(&request), None);
113 }
114
115 #[test]
116 fn basic_http_auth_wrong_header() {
117 let request = Request::fake_http(
118 "GET",
119 "/",
120 vec![("Authorization".to_owned(), "hello world".to_owned())],
121 Vec::new(),
122 );
123 assert_eq!(basic_http_auth(&request), None);
124
125 let request = Request::fake_http(
126 "GET",
127 "/",
128 vec![("Authorization".to_owned(), "Basic \0\0".to_owned())],
129 Vec::new(),
130 );
131 assert_eq!(basic_http_auth(&request), None);
132 }
133
134 #[test]
135 fn basic_http_auth_ok() {
136 let request = Request::fake_http(
137 "GET",
138 "/",
139 vec![(
140 "Authorization".to_owned(),
141 "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_owned(),
142 )],
143 Vec::new(),
144 );
145
146 assert_eq!(
147 basic_http_auth(&request),
148 Some(HttpAuthCredentials {
149 login: "Aladdin".to_owned(),
150 password: "open sesame".to_owned(),
151 })
152 );
153 }
154}