feat: migrate add user's traffic pattern sqlx postgres to use db_*

This commit is contained in:
realaravinth
2022-05-14 18:22:50 +05:30
parent dacdd2cb8e
commit a6920f5f36
3 changed files with 108 additions and 564 deletions

View File

@@ -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,