Handle errors cases when retrieving login token & rate limiting
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pierre HUBERT 2023-04-26 14:11:44 +02:00
parent bee794a589
commit b26e283f7d
2 changed files with 52 additions and 5 deletions

View File

@ -4,9 +4,10 @@ use actix::Addr;
use actix_web::{web, HttpResponse, Responder}; use actix_web::{web, HttpResponse, Responder};
use askama::Template; use askama::Template;
use crate::actors::providers_states_actor; use crate::actors::bruteforce_actor::BruteForceActor;
use crate::actors::providers_states_actor::{ProviderLoginState, ProvidersStatesActor}; use crate::actors::providers_states_actor::{ProviderLoginState, ProvidersStatesActor};
use crate::constants::APP_NAME; use crate::actors::{bruteforce_actor, providers_states_actor};
use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS};
use crate::controllers::base_controller::{build_fatal_error_page, redirect_user}; use crate::controllers::base_controller::{build_fatal_error_page, redirect_user};
use crate::controllers::login_controller::BaseLoginPage; use crate::controllers::login_controller::BaseLoginPage;
use crate::data::action_logger::{Action, ActionLogger}; use crate::data::action_logger::{Action, ActionLogger};
@ -127,6 +128,7 @@ pub async fn finish_login(
remote_ip: RemoteIP, remote_ip: RemoteIP,
providers: web::Data<Arc<ProvidersManager>>, providers: web::Data<Arc<ProvidersManager>>,
states: web::Data<Addr<ProvidersStatesActor>>, states: web::Data<Addr<ProvidersStatesActor>>,
bruteforce: web::Data<Addr<BruteForceActor>>,
query: web::Query<FinishLoginQuery>, query: web::Query<FinishLoginQuery>,
logger: ActionLogger, logger: ActionLogger,
) -> impl Responder { ) -> impl Responder {
@ -167,6 +169,21 @@ pub async fn finish_login(
} }
}; };
// We perform rate limiting before attempting to use authorization code
let failed_attempts = bruteforce
.send(bruteforce_actor::CountFailedAttempt {
ip: remote_ip.into(),
})
.await
.unwrap();
if failed_attempts > MAX_FAILED_LOGIN_ATTEMPTS {
logger.log(Action::ProviderRateLimited);
return HttpResponse::TooManyRequests().body(build_fatal_error_page(
"Too many failed login attempts, please try again later!",
));
}
// Retrieve provider information & configuration // Retrieve provider information & configuration
let provider = providers let provider = providers
.find_by_id(&state.provider_id) .find_by_id(&state.provider_id)
@ -182,11 +199,33 @@ pub async fn finish_login(
} }
}; };
// TODO : rate limiting
// Get access token & user information // Get access token & user information
let token = provider_config.get_token(&provider, &query.code).await; let token = provider_config.get_token(&provider, &query.code).await;
log::debug!("{:#?}", token); let token = match token {
Ok(t) => t,
Err(e) => {
log::error!("Failed to retrieve login token! {:?}", e);
bruteforce
.send(bruteforce_actor::RecordFailedAttempt {
ip: remote_ip.into(),
})
.await
.unwrap();
logger.log(Action::ProviderFailedGetToken {
state: &state,
code: query.code.as_str(),
});
return ProviderLoginError::get(
"Failed to retrieve login token from identity provider!",
&state.redirect,
);
}
};
println!("go on {:?}", token);
// TODO : check token signature // TODO : check token signature
// TODO : check if user is authorized to access application // TODO : check if user is authorized to access application

View File

@ -7,6 +7,7 @@ use actix_identity::Identity;
use actix_web::dev::Payload; use actix_web::dev::Payload;
use actix_web::{web, Error, FromRequest, HttpRequest}; use actix_web::{web, Error, FromRequest, HttpRequest};
use crate::actors::providers_states_actor::ProviderLoginState;
use crate::actors::users_actor; use crate::actors::users_actor;
use crate::actors::users_actor::{AuthorizedAuthenticationSources, UsersActor}; use crate::actors::users_actor::{AuthorizedAuthenticationSources, UsersActor};
use crate::data::client::Client; use crate::data::client::Client;
@ -38,6 +39,11 @@ pub enum Action<'a> {
ProviderCBInvalidState { ProviderCBInvalidState {
state: &'a str, state: &'a str,
}, },
ProviderRateLimited,
ProviderFailedGetToken {
state: &'a ProviderLoginState,
code: &'a str,
},
Signout, Signout,
UserNeed2FAOnLogin(&'a User), UserNeed2FAOnLogin(&'a User),
UserSuccessfullyAuthenticated(&'a User), UserSuccessfullyAuthenticated(&'a User),
@ -108,6 +114,8 @@ impl<'a> Action<'a> {
format!("failed provider authentication with message '{message}'"), format!("failed provider authentication with message '{message}'"),
Action::ProviderCBInvalidState { state } => Action::ProviderCBInvalidState { state } =>
format!("provided invalid callback state after provider authentication: '{state}'"), format!("provided invalid callback state after provider authentication: '{state}'"),
Action::ProviderRateLimited => "could not complete OpenID login because it has reached failed attempts rate limit!".to_string(),
Action::ProviderFailedGetToken {state, code} => format!("could not complete login from provider because the id_token could not be retrieved! (state={:?} code = {code})",state),
Action::Signout => "signed out".to_string(), Action::Signout => "signed out".to_string(),
Action::UserNeed2FAOnLogin(user) => { Action::UserNeed2FAOnLogin(user) => {
format!( format!(