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
96
97
98
99
//! A [Rocket](https://github.com/SergioBenitez/Rocket) library to provide a base for
//! Basic Authentication over which a concrete authentication mechanism can be built.
//!
//! This library exports [BasicAuthRaw] which you could directly use on the request handler.
//! # Example
//!
//! ```
//! use basic_auth_raw::BasicAuthRaw;
//!
//! #[get("/secure-path")
//! fn secret(basic: BasicAuthRaw) -> String {
//!     format!("Your username is {}", basic.username);
//! }
//! ```
//!
//! Or you could build Request Guards on top of it (Recommended).
//! # Example
//!
//! ```
//! use basic_auth_raw::BasicAuthRaw;
//!
//! struct Admin(User);
//!
//! impl<'a, 'r> FromRequest<'a, 'r> for Admin {
//!     type Error = ();
//!
//!     fn from_request(request: &Request) -> Outcome<Self, Self::Error> {
//!         let basic = BasicAuthRaw::from_request(request)?;
//!         let user = User::from_db(basic.username, basic.password);
//!         if user.is_admin {
//!             Outcome::Success(user);
//!         } else {
//!             Outcome::Failure((Status::Unauthorized, ()));
//!         }
//!     }
//! }
//!
//! #[get("/secure-path")
//! fn secret(admin: Admin) -> String {
//!     format!("Your username is {}", admin.user.username);
//! }
//! ```

extern crate rocket;
extern crate base64;

use rocket::{
    Request,
    Outcome,
    outcome::IntoOutcome,
    http::Status,
    request::FromRequest
};

pub struct BasicAuthRaw {
    pub username: String,
    pub password: String,
}

impl<'a, 'r> FromRequest<'a, 'r> for BasicAuthRaw {
    type Error = ();

    fn from_request(request: &Request) -> Outcome<Self, (Status, <Self as FromRequest<'a, 'r>>::Error), ()> {
        let auth_header = request.headers().get_one("Authorization");
        if let Some(auth_header) = auth_header {
            let split = auth_header.split_whitespace().collect::<Vec<_>>();
            if split.len() != 2 {
                return Outcome::Failure((Status::Unauthorized, ()));
            }
            let (basic, payload) = (split[0], split[1]);
            if basic != "Basic" {
                return Outcome::Failure((Status::Unauthorized, ()));
            }
            let decoded = base64::decode(payload)
                .ok()
                .into_outcome((Status::BadRequest, ()))?;

            let decoded_str = String::from_utf8(decoded)
                .ok()
                .into_outcome((Status::BadRequest, ()))?;

            let split = decoded_str.split(":").collect::<Vec<_>>();

            // If exactly username & password pair are present
            if split.len() != 2 {
                return Outcome::Failure((Status::BadRequest, ()));
            }

            let (username, password) = (split[0].to_string(), split[1].to_string());

            Outcome::Success(BasicAuthRaw {
                username,
                password
            })
        } else {
            Outcome::Failure((Status::Unauthorized, ()))
        }
    }
}