summaryrefslogtreecommitdiff
path: root/src/request_handler/handler_login.rs
blob: ac0c37eac3fe3869dd8f49f65989cf46ce41a5af (plain)
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
use std::ops::Add;
use std::io;
use std::borrow::Cow;
use std::time;
use std::thread;
use std::sync::atomic;

use tokio::prelude::*;

use http::{Request, Response, StatusCode, Method};
use http::header::{SET_COOKIE, COOKIE};
use url::form_urlencoded;

use ::ApplicationState;
use ::totp;
use super::*;

pub(in super) fn GET<'a>(header_infos: &HeaderExtract, state: &ApplicationState, path_rest: &'a str)
                         -> Response<String> {
    if is_logged_in(&header_infos.cookies, &state.cookie_store) {
        Response::builder().set_defaults().body(views::login_is_logged_in()).unwrap()
    } else {
        Response::builder().set_defaults().body(views::login_login_form(path_rest)).unwrap()
    }
}

fn test_secrets(secrets: &Vec<&str>, token: &String) -> bool {
    secrets.iter()
        .any(|secret| {
            match totp::verify(secret, token) {
                Ok(true) => true,
                Ok(false) => false,
                Err(e) => {
                    error!("Error from totp::verify: {}", e);
                    false
                }
            }
        })
}

pub(in super) fn POST<'a>(header_infos: &HeaderExtract, state: &ApplicationState, req: &Request<Bytes>)
                          -> Response<String> {
let wait_until = state.request_slowdown.load(atomic::Ordering::Acquire);
    let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_secs();

    let slept = if wait_until > now {
        let time = wait_until - now;
        warn!("Sleep {}s", time);
        thread::sleep(time::Duration::from_secs(time));
        time
    } else {
        0
    };

    let mut token = None;
    let mut redirect = None;
    for (key, val) in form_urlencoded::parse(req.body()) {
        if key == "token" {
            token = Some(val.into_owned())
        } else if key == "redirect" {
            redirect = Some(val.into_owned())
        }
    }
    if token.is_none() {
        return error_handler_internal("missing argument 'token'".to_string());
    }
    let redirect = redirect.unwrap_or(Default::default());

    if header_infos.totp_secrets.is_empty() {
        return error_handler_internal("no secrets configured".to_string());
    }

    if test_secrets(&header_infos.totp_secrets, &token.unwrap()) {
        let cookie_value = state.cookie_store.create_authenticated_cookie();
        let cookie = CookieBuilder::new(COOKIE_NAME, cookie_value.to_string())
            .http_only(true)
            .path("/")
            .max_age(state.cookie_max_age)
            .finish();
        warn!("Authenticated user with cookie {}", cookie);
        Response::builder()
            .set_defaults()
            .header(SET_COOKIE, cookie.to_string())
            .body(views::login_auth_success(&redirect)).unwrap()
    } else {
        let current_wait = state.request_slowdown.load(atomic::Ordering::Acquire);
        let wait_until = time::SystemTime::now()
            .add(time::Duration::from_secs(8))
            .duration_since(time::UNIX_EPOCH).unwrap()
            .as_secs();
        // if this request was already delayed then we double-delay
        let wait_until = wait_until.max(current_wait + 8 + slept);
        state.request_slowdown.store(wait_until, atomic::Ordering::Release);

        Response::builder()
            .set_defaults()
            .body(views::login_auth_fail()).unwrap()
    }
}