ilmen_http/http/security/
service.rs

1use anyhow::Context;
2use base64::{engine::general_purpose::URL_SAFE, Engine};
3use std::str;
4use crate::{http::HttpError, HTTPRequest, Route};
5
6pub fn apply_security(request: &HTTPRequest, route: Route, security: SecurityProtocol) -> Result<Route, HttpError> {
7    match security {
8        SecurityProtocol::None => Ok(()),
9        SecurityProtocol::Basic(validate_methode) => base_auth(request, validate_methode),
10    }.map(|_| route)
11}
12
13fn base_auth(request: &HTTPRequest, validate_methode: AuthMethod) -> Result<(), HttpError>{
14    let header_auth_value : Vec<String> = request.get_header("Authorization")
15        .ok_or(HttpError::UnauthorizedError("Missing header".to_string()))?
16        .1
17        .split(' ')
18        .map(str::to_owned)
19        .collect();
20
21    let is_basic_protocol = header_auth_value.first()
22        .ok_or(HttpError::UnauthorizedError("No protocol specified".to_string()))
23        .map(|protocol| protocol.eq(&"Basic"))?;
24
25    if !is_basic_protocol {
26        return Err(HttpError::UnauthorizedError("Wrong Protocol".to_string()));
27    }
28
29    header_auth_value
30            .get(1)
31            .context("No user password provided")
32            .and_then(decode_base64_auth)
33            .map_err(|e| HttpError::UnauthorizedError(e.to_string()))
34            .map(validate_methode)
35            .and_then(|is_valid_creds| {
36                match is_valid_creds {
37                    true => Ok(()),
38                    false => Err(HttpError::UnauthorizedError("Unauthorized".to_string())),
39                }
40            })
41}
42
43type Username = String;
44type Password = String;
45type AuthMethod = fn((Username, Password)) -> bool;
46
47fn decode_base64_auth(b64_value: &String) -> anyhow::Result<(String, String)>{
48    URL_SAFE.decode(b64_value)
49            .context("Authentication parameter is not base64 encrypted".to_string())
50            .and_then(|vect| String::from_utf8(vect).context("Could not parse from utf8"))
51            .map(|decoded | {
52                let splitter = decoded.split_once(':').unwrap_or(("", ""));
53                (splitter.0.to_string(), splitter.1.to_string())
54            })
55}
56
57#[derive(Clone)]
58pub enum SecurityProtocol {
59    None,
60    Basic(AuthMethod)
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn apply_basic_security_with_good_creds() {
69        let buffer = "GET rappel/1 HTTP/1.1\r\nAuthorization: Basic dG90bzp0YXRh\r\n\r\ntoto";
70        
71        let  request = HTTPRequest::try_from(buffer).unwrap();
72        let validate : AuthMethod= |_| true;
73
74        let result = apply_security(&request, Route::default(), SecurityProtocol::Basic(validate)).unwrap();
75        assert_eq!(result, Route::default())
76    }
77
78    #[test]
79    fn apply_basic_security_with_bad_creds() {
80        let buffer = "GET rappel/1 HTTP/1.1\r\nAuthorization: Basic dG90bzp0YXR1YWE=\r\n\r\ntoto";
81        
82        let  request = HTTPRequest::try_from(buffer).unwrap();
83
84        let validate : AuthMethod = |_| false;
85
86        let result = apply_security(&request, Route::default(), SecurityProtocol::Basic(validate)).unwrap_err();
87        assert_eq!(result, HttpError::UnauthorizedError("Unauthorized".to_string()))
88    }
89
90    #[test]
91    fn apply_basic_security_with_bad_protocol() {
92        let buffer = "GET rappel/1 HTTP/1.1\r\nAuthorization: Basics dG90bzp0YXR1YWE=\r\n\r\ntoto";
93        
94        let  request = HTTPRequest::try_from(buffer).unwrap();
95
96        let validate : AuthMethod = |_| false;
97
98        let result = apply_security(&request, Route::default(), SecurityProtocol::Basic(validate)).unwrap_err();
99        assert_eq!(result, HttpError::UnauthorizedError("Wrong Protocol".to_string()))
100    }
101}