From 3b8051159d28d2942a3dafa3c4b02d0a3687157b Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Thu, 4 Jan 2024 23:29:20 +0530 Subject: [PATCH] feat: use time for easy captcha when option is configured by admin --- src/api/v1/mcaptcha/easy.rs | 111 +++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/src/api/v1/mcaptcha/easy.rs b/src/api/v1/mcaptcha/easy.rs index 65698aef..14636447 100644 --- a/src/api/v1/mcaptcha/easy.rs +++ b/src/api/v1/mcaptcha/easy.rs @@ -101,6 +101,79 @@ pub fn calculate( Ok(levels) } +async fn calculate_with_percentile( + data: &AppData, + tp: &TrafficPattern, +) -> ServiceResult>> { + use crate::api::v1::stats::{percentile_bench_runner, PercentileReq}; + + let strategy = &data.settings.captcha.default_difficulty_strategy; + + if strategy.avg_traffic_time.is_none() + && strategy.peak_sustainable_traffic_time.is_none() + && strategy.broke_my_site_traffic_time.is_none() + { + return Ok(None); + } + + let mut req = PercentileReq { + time: strategy.avg_traffic_time.unwrap(), + percentile: 90.00, + }; + let resp = percentile_bench_runner(data, &req).await?; + if resp.difficulty_factor.is_none() { + return Ok(None); + } + let avg_traffic_difficulty = resp.difficulty_factor.unwrap(); + + req.time = strategy.peak_sustainable_traffic_time.unwrap(); + let resp = percentile_bench_runner(data, &req).await?; + if resp.difficulty_factor.is_none() { + return Ok(None); + } + let peak_sustainable_traffic_difficulty = resp.difficulty_factor.unwrap(); + + req.time = strategy.broke_my_site_traffic_time.unwrap(); + let resp = percentile_bench_runner(data, &req).await?; + let broke_my_site_traffic_difficulty = if resp.difficulty_factor.is_none() { + resp.difficulty_factor.unwrap() + } else { + peak_sustainable_traffic_difficulty * 2 + }; + + let mut levels = vec![ + LevelBuilder::default() + .difficulty_factor(avg_traffic_difficulty)? + .visitor_threshold(tp.avg_traffic) + .build()?, + LevelBuilder::default() + .difficulty_factor(peak_sustainable_traffic_difficulty)? + .visitor_threshold(tp.peak_sustainable_traffic) + .build()?, + ]; + let mut highest_level = LevelBuilder::default(); + highest_level.difficulty_factor(broke_my_site_traffic_difficulty)?; + + match tp.broke_my_site_traffic { + Some(broke_my_site_traffic) => { + highest_level.visitor_threshold(broke_my_site_traffic) + } + None => match tp + .peak_sustainable_traffic + .checked_add(tp.peak_sustainable_traffic / 2) + { + Some(num) => highest_level.visitor_threshold(num), + // TODO check for overflow: database saves these values as i32, so this u32 is cast + // into i32. Should choose bigger number or casts properly + None => highest_level.visitor_threshold(u32::MAX), + }, + }; + + levels.push(highest_level.build()?); + + Ok(Some(levels)) +} + #[my_codegen::post( path = "crate::V1_API_ROUTES.captcha.easy.create", wrap = "crate::api::v1::get_middleware()" @@ -113,8 +186,12 @@ async fn create( let username = id.identity().unwrap(); let payload = payload.into_inner(); let pattern = (&payload).into(); - let levels = - calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)?; + let levels = if let Some(levels) = calculate_with_percentile(&data, &pattern).await? + { + levels + } else { + calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)? + }; let msg = CreateCaptcha { levels, duration: data.settings.captcha.default_difficulty_strategy.duration, @@ -170,6 +247,36 @@ async fn update( Ok(HttpResponse::Ok()) } +async fn update_runner( + payload: web::Json, + data: AppData, + id: Identity, +) -> ServiceResult { + let username = id.identity().unwrap(); + let payload = payload.into_inner(); + let pattern = (&payload.pattern).into(); + let levels = + calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)?; + + let msg = UpdateCaptcha { + levels, + duration: data.settings.captcha.default_difficulty_strategy.duration, + description: payload.pattern.description, + key: payload.key, + publish_benchmarks: payload.pattern.publish_benchmarks, + }; + + update_captcha_runner(&msg, &data, &username).await?; + + data.db.delete_traffic_pattern(&username, &msg.key).await?; + + data.db + .add_traffic_pattern(&username, &msg.key, &pattern) + .await?; + + Ok(HttpResponse::Ok()) +} + #[cfg(test)] pub mod tests { use actix_web::http::StatusCode;