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}