#
mcaptcha/api/v1/mcaptcha/
create.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::{web, HttpResponse, Responder};
    8use libmcaptcha::defense::Level;
    9use serde::{Deserialize, Serialize};
    10
    11use db_core::errors::DBError;
    12use db_core::CreateCaptcha as DBCreateCaptcha;
    13
    14use super::get_random;
    15use crate::errors::*;
    16use crate::AppData;
    17
    18#[derive(Serialize, Deserialize)]
    19pub struct CreateCaptcha {
    20    pub levels: Vec<Level>,
    21    pub duration: u32,
    22    pub description: String,
    23    pub publish_benchmarks: bool,
    24}
    25
    26#[derive(Clone, Debug, Deserialize, Serialize)]
    27pub struct MCaptchaDetails {
    28    pub name: String,
    29    pub key: String,
    30}
    31
    32// TODO redo mcaptcha table to include levels as json field
    33// so that the whole thing can be added/udpaed in a single stroke
    34#[my_codegen::post(
    35    path = "crate::V1_API_ROUTES.captcha.create",
    36    wrap = "crate::api::v1::get_middleware()"
    37)]
    38pub async fn create(
    39    payload: web::Json<CreateCaptcha>,
    40    data: AppData,
    41    id: Identity,
    42) -> ServiceResult<impl Responder> {
    43    let username = id.identity().unwrap();
    44    let mcaptcha_config = runner::create(&payload, &data, &username).await?;
    45    if payload.publish_benchmarks {
    46        data.db
    47            .analytics_create_psuedo_id_if_not_exists(&mcaptcha_config.key)
    48            .await?;
    49    }
    50    Ok(HttpResponse::Ok().json(mcaptcha_config))
    51}
    52
    53pub mod runner {
    54    use super::*;
    55    use libmcaptcha::DefenseBuilder;
    56
    57    pub async fn create(
    58        payload: &CreateCaptcha,
    59        data: &AppData,
    60        username: &str,
    61    ) -> ServiceResult<MCaptchaDetails> {
    62        let mut defense = DefenseBuilder::default();
    63        for level in payload.levels.iter() {
    64            defense.add_level(*level)?;
    65        }
    66
    67        defense.build()?;
    68
    69        let mut key;
    70        let duration = payload.duration as i32;
    71        loop {
    72            key = get_random(32);
    73            let p = DBCreateCaptcha {
    74                description: &payload.description,
    75                key: &key,
    76                duration,
    77            };
    78
    79            match data.db.create_captcha(username, &p).await {
    80                Ok(_) => break,
    81                Err(DBError::SecretTaken) => continue,
    82                Err(e) => return Err(e.into()),
    83            }
    84        }
    85        data.db
    86            .add_captcha_levels(username, &key, &payload.levels)
    87            .await?;
    88
    89        if payload.publish_benchmarks {
    90            data.db
    91                .analytics_create_psuedo_id_if_not_exists(&key)
    92                .await?;
    93        }
    94
    95        let mcaptcha_config = MCaptchaDetails {
    96            name: payload.description.clone(),
    97            key,
    98        };
    99
    100        Ok(mcaptcha_config)
    101    }
    102}