#
mcaptcha/pages/panel/sitekey/
edit.rs

    1// Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
    2// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
    3//
    4// SPDX-License-Identifier: AGPL-3.0-or-later
    5
    6use actix_identity::Identity;
    7use actix_web::{http, web, HttpResponse, Responder};
    8use sailfish::TemplateOnce;
    9
    10use db_core::errors::DBError;
    11use db_core::Captcha;
    12use libmcaptcha::defense::Level;
    13
    14use crate::api::v1::mcaptcha::easy::TrafficPatternRequest;
    15use crate::errors::*;
    16use crate::AppData;
    17
    18const PAGE: &str = "Edit Sitekey";
    19
    20#[derive(TemplateOnce, Clone)]
    21#[template(path = "panel/sitekey/edit/advance.html")]
    22struct AdvanceEditPage {
    23    duration: u32,
    24    name: String,
    25    key: String,
    26    levels: Vec<Level>,
    27    publish_benchmarks: bool,
    28}
    29
    30impl AdvanceEditPage {
    31    fn new(
    32        config: Captcha,
    33        levels: Vec<Level>,
    34        key: String,
    35        publish_benchmarks: bool,
    36    ) -> Self {
    37        AdvanceEditPage {
    38            duration: config.duration as u32,
    39            name: config.description,
    40            levels,
    41            key,
    42            publish_benchmarks,
    43        }
    44    }
    45}
    46
    47/// route handler that renders individual views for sitekeys
    48#[my_codegen::get(
    49    path = "crate::PAGES.panel.sitekey.edit_advance",
    50    wrap = "crate::pages::get_middleware()"
    51)]
    52pub async fn advance(
    53    path: web::Path<String>,
    54    data: AppData,
    55    id: Identity,
    56) -> PageResult<impl Responder> {
    57    let username = id.identity().unwrap();
    58    let key = path.into_inner();
    59
    60    let config = data.db.get_captcha_config(&username, &key).await?;
    61    let levels = data.db.get_captcha_levels(Some(&username), &key).await?;
    62    let publish_benchmarks = data.db.analytics_captcha_is_published(&key).await?;
    63
    64    let body = AdvanceEditPage::new(config, levels, key, publish_benchmarks)
    65        .render_once()
    66        .unwrap();
    67    Ok(HttpResponse::Ok()
    68        .content_type("text/html; charset=utf-8")
    69        .body(body))
    70}
    71
    72#[derive(TemplateOnce, Clone)]
    73#[template(path = "panel/sitekey/edit/easy/index.html")]
    74pub struct EasyEditPage<'a> {
    75    pub form_title: &'a str,
    76    pub pattern: TrafficPatternRequest,
    77    pub key: String,
    78}
    79
    80impl<'a> EasyEditPage<'a> {
    81    pub fn new(key: String, pattern: TrafficPatternRequest) -> Self {
    82        Self {
    83            form_title: PAGE,
    84            pattern,
    85            key,
    86        }
    87    }
    88}
    89
    90/// route handler that renders individual views for sitekeys
    91#[my_codegen::get(
    92    path = "crate::PAGES.panel.sitekey.edit_easy",
    93    wrap = "crate::pages::get_middleware()"
    94)]
    95pub async fn easy(
    96    path: web::Path<String>,
    97    data: AppData,
    98    id: Identity,
    99) -> PageResult<impl Responder> {
    100    let username = id.identity().unwrap();
    101    let key = path.into_inner();
    102
    103    match data.db.get_traffic_pattern(&username, &key).await {
    104        Ok(c) => {
    105            let config = data.db.get_captcha_config(&username, &key).await?;
    106            let publish_benchmarks =
    107                data.db.analytics_captcha_is_published(&key).await?;
    108            let pattern = TrafficPatternRequest {
    109                peak_sustainable_traffic: c.peak_sustainable_traffic,
    110                avg_traffic: c.avg_traffic,
    111                broke_my_site_traffic: c.broke_my_site_traffic,
    112                description: config.description,
    113                publish_benchmarks,
    114            };
    115
    116            let page = EasyEditPage::new(key, pattern).render_once().unwrap();
    117            return Ok(HttpResponse::Ok()
    118                .content_type("text/html; charset=utf-8")
    119                .body(page));
    120        }
    121        Err(DBError::TrafficPatternNotFound) => {
    122            return Ok(HttpResponse::Found()
    123                .insert_header((
    124                    http::header::LOCATION,
    125                    crate::PAGES.panel.sitekey.get_edit_advance(&key),
    126                ))
    127                .finish());
    128        }
    129        Err(e) => {
    130            let e: ServiceError = e.into();
    131            Err(e.into())
    132        }
    133    }
    134}
    135
    136#[cfg(test)]
    137mod test {
    138    use actix_web::http::{header, StatusCode};
    139    use actix_web::test;
    140    use actix_web::web::Bytes;
    141
    142    use crate::tests::*;
    143    use crate::*;
    144
    145    #[actix_rt::test]
    146    async fn edit_sitekey_work_pg_test() {
    147        let data = pg::get_data().await;
    148        edit_sitekey_work(data).await;
    149    }
    150
    151    #[actix_rt::test]
    152    async fn edit_sitekey_work_maria_test() {
    153        let data = maria::get_data().await;
    154        edit_sitekey_work(data).await;
    155    }
    156
    157    async fn edit_sitekey_work(data: ArcData) {
    158        const NAME: &str = "editsitekeyuser";
    159        const PASSWORD: &str = "longpassworddomain";
    160        const EMAIL: &str = "editsitekeyuser@a.com";
    161        let data = &data;
    162        delete_user(data, NAME).await;
    163
    164        register_and_signin(data, NAME, EMAIL, PASSWORD).await;
    165        let (_, signin_resp, key) = add_levels_util(data, NAME, PASSWORD).await;
    166        let cookies = get_cookie!(signin_resp);
    167
    168        let app = get_app!(data).await;
    169
    170        let url = PAGES.panel.sitekey.get_edit_advance(&key.key);
    171
    172        let list_sitekey_resp = test::call_service(
    173            &app,
    174            test::TestRequest::get()
    175                .uri(&url)
    176                .cookie(cookies.clone())
    177                .to_request(),
    178        )
    179        .await;
    180
    181        assert_eq!(list_sitekey_resp.status(), StatusCode::OK);
    182
    183        let body: Bytes = test::read_body(list_sitekey_resp).await;
    184        let body = String::from_utf8(body.to_vec()).unwrap();
    185
    186        assert!(body.contains(&key.name));
    187
    188        assert!(body.contains(&L1.visitor_threshold.to_string()));
    189        assert!(body.contains(&L1.difficulty_factor.to_string()));
    190        assert!(body.contains(&L2.difficulty_factor.to_string()));
    191        assert!(body.contains(&L2.visitor_threshold.to_string()));
    192
    193        let easy_url = PAGES.panel.sitekey.get_edit_easy(&key.key);
    194
    195        let redirect_resp = test::call_service(
    196            &app,
    197            test::TestRequest::get()
    198                .uri(&easy_url)
    199                .cookie(cookies.clone())
    200                .to_request(),
    201        )
    202        .await;
    203        assert_eq!(redirect_resp.status(), StatusCode::FOUND);
    204        let headers = redirect_resp.headers();
    205        assert_eq!(headers.get(header::LOCATION).unwrap(), &url);
    206    }
    207}