Enable bruteforce protection on login endpoint

This commit is contained in:
2022-04-03 17:33:01 +02:00
parent 9943df4952
commit 886bae32c8
9 changed files with 209 additions and 38 deletions

View File

@ -1,6 +1,7 @@
use std::fmt::Display;
use actix_web::HttpResponse;
use askama::Template;
use crate::constants::LOGIN_ROUTE;
@ -24,3 +25,10 @@ pub fn redirect_user_for_login<P: Display>(redirect_uri: P) -> HttpResponse {
))
.finish()
}
/// Fatal error page message
#[derive(Template)]
#[template(path = "fatal_error.html")]
pub struct FatalErrorPage {
pub message: &'static str,
}

View File

@ -1,13 +1,16 @@
use actix::Addr;
use actix_identity::Identity;
use actix_web::{web, HttpResponse, Responder};
use actix_web::{HttpRequest, HttpResponse, Responder, web};
use askama::Template;
use crate::actors::users_actor;
use crate::actors::{bruteforce_actor, users_actor};
use crate::actors::bruteforce_actor::BruteForceActor;
use crate::actors::users_actor::{ChangePasswordResult, LoginResult, UsersActor};
use crate::constants::{APP_NAME, MIN_PASS_LEN};
use crate::controllers::base_controller::redirect_user;
use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS, MIN_PASS_LEN};
use crate::controllers::base_controller::{FatalErrorPage, redirect_user};
use crate::data::app_config::AppConfig;
use crate::data::session_identity::{SessionIdentity, SessionStatus};
use crate::utils::network_utils::{get_remote_ip, parse_ip};
#[derive(Template)]
#[template(path = "base_login_page.html")]
@ -47,15 +50,38 @@ pub struct LoginRequestQuery {
/// Authenticate user
pub async fn login_route(
http_req: HttpRequest,
users: web::Data<Addr<UsersActor>>,
bruteforce: web::Data<Addr<BruteForceActor>>,
query: web::Query<LoginRequestQuery>,
req: Option<web::Form<LoginRequestBody>>,
config: web::Data<AppConfig>,
id: Identity,
) -> impl Responder {
let mut danger = String::new();
let mut success = String::new();
let mut login = String::new();
let remote_ip = match parse_ip(&get_remote_ip(&http_req, config.proxy_ip.as_deref())) {
None => return HttpResponse::InternalServerError().body(
FatalErrorPage {
message: "Failed to determine remote ip address!"
}.render().unwrap()
),
Some(i) => i,
};
let failed_attempts = bruteforce.send(bruteforce_actor::CountFailedAttempt { ip: remote_ip })
.await.unwrap();
if failed_attempts > MAX_FAILED_LOGIN_ATTEMPTS {
return HttpResponse::InternalServerError().body(
FatalErrorPage {
message: "Too many failed login attempts, please try again later!"
}.render().unwrap()
);
}
let redirect_uri = match query.redirect.as_deref() {
None => "/",
Some(s) => match s.starts_with('/') && !s.starts_with("//") {
@ -125,8 +151,10 @@ pub async fn login_route(
}
c => {
log::warn!("Failed login for username {} : {:?}", login, c);
log::warn!("Failed login for ip {:?} / username {}: {:?}", remote_ip, login, c);
danger = "Login failed.".to_string();
bruteforce.send(bruteforce_actor::RecordFailedAttempt { ip: remote_ip }).await.unwrap();
}
}
}
@ -144,8 +172,8 @@ pub async fn login_route(
},
min_pass_len: MIN_PASS_LEN,
}
.render()
.unwrap(),
.render()
.unwrap(),
);
}
@ -160,8 +188,8 @@ pub async fn login_route(
},
login,
}
.render()
.unwrap(),
.render()
.unwrap(),
)
}