mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2026-02-11 10:05:41 +00:00
feat: migrate add user's traffic pattern sqlx postgres to use db_*
This commit is contained in:
@@ -1,24 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use actix_identity::Identity;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use libmcaptcha::{defense::Level, defense::LevelBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use db_core::TrafficPattern;
|
||||
|
||||
use super::create::{runner::create as create_runner, CreateCaptcha};
|
||||
use super::update::{runner::update_captcha as update_captcha_runner, UpdateCaptcha};
|
||||
use crate::errors::*;
|
||||
@@ -47,103 +49,98 @@ pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(create);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct TrafficPattern {
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
/// User's traffic pattern; used in generating a captcha configuration
|
||||
pub struct TrafficPatternRequest {
|
||||
/// average traffic of user's website
|
||||
pub avg_traffic: u32,
|
||||
/// the peak traffic that the user's website can handle
|
||||
pub peak_sustainable_traffic: u32,
|
||||
/// trafic that bought the user's website down; optional
|
||||
pub broke_my_site_traffic: Option<u32>,
|
||||
/// Captcha description
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl TrafficPattern {
|
||||
pub fn calculate(
|
||||
&self,
|
||||
strategy: &DefaultDifficultyStrategy,
|
||||
) -> ServiceResult<Vec<Level>> {
|
||||
let mut levels = vec![
|
||||
LevelBuilder::default()
|
||||
.difficulty_factor(strategy.avg_traffic_difficulty)?
|
||||
.visitor_threshold(self.avg_traffic)
|
||||
.build()?,
|
||||
LevelBuilder::default()
|
||||
.difficulty_factor(strategy.peak_sustainable_traffic_difficulty)?
|
||||
.visitor_threshold(self.peak_sustainable_traffic)
|
||||
.build()?,
|
||||
];
|
||||
let mut highest_level = LevelBuilder::default();
|
||||
highest_level.difficulty_factor(strategy.broke_my_site_traffic_difficulty)?;
|
||||
|
||||
match self.broke_my_site_traffic {
|
||||
Some(broke_my_site_traffic) => {
|
||||
highest_level.visitor_threshold(broke_my_site_traffic)
|
||||
}
|
||||
None => match self
|
||||
.peak_sustainable_traffic
|
||||
.checked_add(self.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(levels)
|
||||
impl From<&TrafficPatternRequest> for TrafficPattern {
|
||||
fn from(t: &TrafficPatternRequest) -> Self {
|
||||
TrafficPattern {
|
||||
avg_traffic: t.avg_traffic,
|
||||
peak_sustainable_traffic: t.peak_sustainable_traffic,
|
||||
broke_my_site_traffic: t.broke_my_site_traffic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculate(
|
||||
tp: &TrafficPattern,
|
||||
strategy: &DefaultDifficultyStrategy,
|
||||
) -> ServiceResult<Vec<Level>> {
|
||||
let mut levels = vec![
|
||||
LevelBuilder::default()
|
||||
.difficulty_factor(strategy.avg_traffic_difficulty)?
|
||||
.visitor_threshold(tp.avg_traffic)
|
||||
.build()?,
|
||||
LevelBuilder::default()
|
||||
.difficulty_factor(strategy.peak_sustainable_traffic_difficulty)?
|
||||
.visitor_threshold(tp.peak_sustainable_traffic)
|
||||
.build()?,
|
||||
];
|
||||
let mut highest_level = LevelBuilder::default();
|
||||
highest_level.difficulty_factor(strategy.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(levels)
|
||||
}
|
||||
|
||||
#[my_codegen::post(
|
||||
path = "crate::V1_API_ROUTES.captcha.easy.create",
|
||||
wrap = "crate::api::v1::get_middleware()"
|
||||
)]
|
||||
async fn create(
|
||||
payload: web::Json<TrafficPattern>,
|
||||
payload: web::Json<TrafficPatternRequest>,
|
||||
data: AppData,
|
||||
id: Identity,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
let username = id.identity().unwrap();
|
||||
let payload = payload.into_inner();
|
||||
let levels =
|
||||
payload.calculate(&crate::SETTINGS.captcha.default_difficulty_strategy)?;
|
||||
let pattern = (&payload).into();
|
||||
let levels = calculate(
|
||||
&pattern,
|
||||
&crate::SETTINGS.captcha.default_difficulty_strategy,
|
||||
)?;
|
||||
let msg = CreateCaptcha {
|
||||
levels,
|
||||
duration: crate::SETTINGS.captcha.default_difficulty_strategy.duration,
|
||||
description: payload.description,
|
||||
};
|
||||
|
||||
let broke_my_site_traffic = payload.broke_my_site_traffic.map(|n| n as i32);
|
||||
|
||||
let mcaptcha_config = create_runner(&msg, &data, &username).await?;
|
||||
sqlx::query!(
|
||||
"INSERT INTO mcaptcha_sitekey_user_provided_avg_traffic (
|
||||
config_id,
|
||||
avg_traffic,
|
||||
peak_sustainable_traffic,
|
||||
broke_my_site_traffic
|
||||
) VALUES (
|
||||
(SELECT config_id FROM mcaptcha_config
|
||||
WHERE
|
||||
key = ($1)
|
||||
AND user_id = (SELECT ID FROM mcaptcha_users WHERE name = $2)
|
||||
), $3, $4, $5)",
|
||||
//payload.avg_traffic,
|
||||
&mcaptcha_config.key,
|
||||
&username,
|
||||
payload.avg_traffic as i32,
|
||||
payload.peak_sustainable_traffic as i32,
|
||||
broke_my_site_traffic,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
|
||||
data.dblib
|
||||
.add_traffic_pattern(&username, &mcaptcha_config.key, &pattern)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(mcaptcha_config))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct UpdateTrafficPattern {
|
||||
pub pattern: TrafficPattern,
|
||||
pub pattern: TrafficPatternRequest,
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
@@ -158,9 +155,11 @@ async fn update(
|
||||
) -> ServiceResult<impl Responder> {
|
||||
let username = id.identity().unwrap();
|
||||
let payload = payload.into_inner();
|
||||
let levels = payload
|
||||
.pattern
|
||||
.calculate(&crate::SETTINGS.captcha.default_difficulty_strategy)?;
|
||||
let pattern = (&payload.pattern).into();
|
||||
let levels = calculate(
|
||||
&pattern,
|
||||
&crate::SETTINGS.captcha.default_difficulty_strategy,
|
||||
)?;
|
||||
|
||||
let msg = UpdateCaptcha {
|
||||
levels,
|
||||
@@ -188,29 +187,9 @@ async fn update(
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
|
||||
let broke_my_site_traffic = payload.pattern.broke_my_site_traffic.map(|n| n as i32);
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO mcaptcha_sitekey_user_provided_avg_traffic (
|
||||
config_id,
|
||||
avg_traffic,
|
||||
peak_sustainable_traffic,
|
||||
broke_my_site_traffic
|
||||
) VALUES (
|
||||
(SELECT config_id FROM mcaptcha_config
|
||||
WHERE
|
||||
key = ($1)
|
||||
AND user_id = (SELECT ID FROM mcaptcha_users WHERE name = $2)
|
||||
), $3, $4, $5)",
|
||||
//payload.avg_traffic,
|
||||
&msg.key,
|
||||
&username,
|
||||
payload.pattern.avg_traffic as i32,
|
||||
payload.pattern.peak_sustainable_traffic as i32,
|
||||
broke_my_site_traffic,
|
||||
)
|
||||
.execute(&data.db)
|
||||
.await?;
|
||||
data.dblib
|
||||
.add_traffic_pattern(&username, &msg.key, &pattern)
|
||||
.await?;
|
||||
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
@@ -228,7 +207,9 @@ pub mod tests {
|
||||
use crate::*;
|
||||
|
||||
mod isoloated_test {
|
||||
use super::{LevelBuilder, TrafficPattern};
|
||||
use super::{calculate, LevelBuilder};
|
||||
|
||||
use db_core::TrafficPattern;
|
||||
|
||||
#[test]
|
||||
fn easy_configuration_works() {
|
||||
@@ -238,7 +219,6 @@ pub mod tests {
|
||||
avg_traffic: 100_000,
|
||||
peak_sustainable_traffic: 1_000_000,
|
||||
broke_my_site_traffic: Some(10_000_000),
|
||||
description: NAME.into(),
|
||||
};
|
||||
|
||||
let strategy = &crate::SETTINGS.captcha.default_difficulty_strategy;
|
||||
@@ -263,7 +243,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
|
||||
let levels = vec![l1, l2, l3];
|
||||
assert_eq!(payload.calculate(strategy).unwrap(), levels);
|
||||
assert_eq!(calculate(&payload, strategy).unwrap(), levels);
|
||||
|
||||
let estimated_lmax = LevelBuilder::default()
|
||||
.difficulty_factor(strategy.broke_my_site_traffic_difficulty)
|
||||
@@ -273,7 +253,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
payload.broke_my_site_traffic = None;
|
||||
assert_eq!(
|
||||
payload.calculate(strategy).unwrap(),
|
||||
calculate(&payload, strategy).unwrap(),
|
||||
vec![l1, l2, estimated_lmax]
|
||||
);
|
||||
|
||||
@@ -295,7 +275,7 @@ pub mod tests {
|
||||
// payload.broke_my_site_traffic = Some(very_large_l2_peak_traffic);
|
||||
payload.peak_sustainable_traffic = very_large_l2_peak_traffic;
|
||||
assert_eq!(
|
||||
payload.calculate(strategy).unwrap(),
|
||||
calculate(&payload, strategy).unwrap(),
|
||||
vec![l1, very_large_l2, lmax]
|
||||
);
|
||||
}
|
||||
@@ -316,16 +296,18 @@ pub mod tests {
|
||||
let cookies = get_cookie!(signin_resp);
|
||||
let app = get_app!(data).await;
|
||||
|
||||
let payload = TrafficPattern {
|
||||
let payload = TrafficPatternRequest {
|
||||
avg_traffic: 100_000,
|
||||
peak_sustainable_traffic: 1_000_000,
|
||||
broke_my_site_traffic: Some(10_000_000),
|
||||
description: NAME.into(),
|
||||
};
|
||||
|
||||
let default_levels = payload
|
||||
.calculate(&crate::SETTINGS.captcha.default_difficulty_strategy)
|
||||
.unwrap();
|
||||
let default_levels = calculate(
|
||||
&(&payload).into(),
|
||||
&crate::SETTINGS.captcha.default_difficulty_strategy,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// START create_easy
|
||||
|
||||
@@ -353,16 +335,18 @@ pub mod tests {
|
||||
// END create_easy
|
||||
|
||||
// START update_easy
|
||||
let update_pattern = TrafficPattern {
|
||||
let update_pattern = TrafficPatternRequest {
|
||||
avg_traffic: 1_000,
|
||||
peak_sustainable_traffic: 10_000,
|
||||
broke_my_site_traffic: Some(1_000_000),
|
||||
description: NAME.into(),
|
||||
};
|
||||
|
||||
let updated_default_values = update_pattern
|
||||
.calculate(&crate::SETTINGS.captcha.default_difficulty_strategy)
|
||||
.unwrap();
|
||||
let updated_default_values = calculate(
|
||||
&(&update_pattern).into(),
|
||||
&crate::SETTINGS.captcha.default_difficulty_strategy,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let payload = UpdateTrafficPattern {
|
||||
pattern: update_pattern,
|
||||
|
||||
Reference in New Issue
Block a user