#
mcaptcha/api/v1/pow/
verify_token.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
    6//! PoW success token module
    7
    8use actix_web::{web, HttpResponse, Responder};
    9use libmcaptcha::cache::messages::VerifyCaptchaResult;
    10use serde::{Deserialize, Serialize};
    11
    12use crate::errors::*;
    13use crate::AppData;
    14use crate::V1_API_ROUTES;
    15
    16#[derive(Clone, Debug, Deserialize, Serialize)]
    17pub struct CaptchaValidateResp {
    18    pub valid: bool,
    19}
    20
    21#[derive(Clone, Debug, Deserialize, Serialize)]
    22pub struct VerifyCaptchaResultPayload {
    23    pub secret: String,
    24    pub key: String,
    25    pub token: String,
    26}
    27
    28impl From<VerifyCaptchaResultPayload> for VerifyCaptchaResult {
    29    fn from(m: VerifyCaptchaResultPayload) -> Self {
    30        VerifyCaptchaResult {
    31            token: m.token,
    32            key: m.key,
    33        }
    34    }
    35}
    36
    37// API keys are mcaptcha actor names
    38
    39/// route handler that validates a PoW solution token
    40#[my_codegen::post(path = "V1_API_ROUTES.pow.validate_captcha_token()")]
    41pub async fn validate_captcha_token(
    42    payload: web::Json<VerifyCaptchaResultPayload>,
    43    data: AppData,
    44) -> ServiceResult<impl Responder> {
    45    let secret = data.db.get_secret_from_captcha(&payload.key).await?;
    46    if secret.secret != payload.secret {
    47        return Err(ServiceError::WrongPassword);
    48    }
    49    let payload: VerifyCaptchaResult = payload.into_inner().into();
    50    let key = payload.key.clone();
    51    let res = data.captcha.validate_verification_tokens(payload).await?;
    52    let resp = CaptchaValidateResp { valid: res };
    53    data.stats.record_confirm(&data, &key).await?;
    54    //println!("{:?}", &payload);
    55    Ok(HttpResponse::Ok().json(resp))
    56}
    57
    58#[cfg(test)]
    59pub mod tests {
    60    use actix_web::http::StatusCode;
    61    use actix_web::test;
    62    use libmcaptcha::pow::PoWConfig;
    63    use libmcaptcha::pow::Work;
    64
    65    use super::*;
    66    use crate::api::v1::pow::get_config::GetConfigPayload;
    67    use crate::api::v1::pow::verify_pow::ValidationToken;
    68    use crate::tests::*;
    69    use crate::*;
    70
    71    #[actix_rt::test]
    72    async fn validate_captcha_token_works_pg() {
    73        let data = crate::tests::pg::get_data().await;
    74        validate_captcha_token_works(data).await;
    75    }
    76
    77    #[actix_rt::test]
    78    async fn validate_captcha_token_works_maria() {
    79        let data = crate::tests::maria::get_data().await;
    80        validate_captcha_token_works(data).await;
    81    }
    82
    83    pub async fn validate_captcha_token_works(data: ArcData) {
    84        const NAME: &str = "enterprisetken";
    85        const PASSWORD: &str = "testingpas";
    86        const EMAIL: &str = "verifyuser@enter.com";
    87        const VERIFY_CAPTCHA_URL: &str = "/api/v1/pow/verify";
    88        const GET_URL: &str = "/api/v1/pow/config";
    89        const VERIFY_TOKEN_URL: &str = "/api/v1/pow/siteverify";
    90        //        const UPDATE_URL: &str = "/api/v1/mcaptcha/domain/token/duration/update";
    91
    92        let data = &data;
    93        delete_user(data, NAME).await;
    94
    95        register_and_signin(data, NAME, EMAIL, PASSWORD).await;
    96        let (_, signin_resp, token_key) = add_levels_util(data, NAME, PASSWORD).await;
    97        let app = get_app!(data).await;
    98        let cookies = get_cookie!(signin_resp);
    99
    100        let secret = test::call_service(
    101            &app,
    102            test::TestRequest::get()
    103                .cookie(cookies.clone())
    104                .uri(V1_API_ROUTES.account.get_secret)
    105                .to_request(),
    106        )
    107        .await;
    108        assert_eq!(secret.status(), StatusCode::OK);
    109        let secret: db_core::Secret = test::read_body_json(secret).await;
    110        let secret = secret.secret;
    111
    112        let get_config_payload = GetConfigPayload {
    113            key: token_key.key.clone(),
    114        };
    115
    116        // update and check changes
    117
    118        let get_config_resp = test::call_service(
    119            &app,
    120            post_request!(&get_config_payload, GET_URL).to_request(),
    121        )
    122        .await;
    123        assert_eq!(get_config_resp.status(), StatusCode::OK);
    124        let config: PoWConfig = test::read_body_json(get_config_resp).await;
    125
    126        let pow = mcaptcha_pow_sha256::ConfigBuilder::default()
    127            .salt(config.salt)
    128            .build()
    129            .unwrap();
    130        let work = pow
    131            .prove_work(&config.string.clone(), config.difficulty_factor)
    132            .unwrap();
    133
    134        let work = Work {
    135            string: config.string.clone(),
    136            result: work.result,
    137            nonce: work.nonce,
    138            key: token_key.key.clone(),
    139        };
    140
    141        let pow_verify_resp = test::call_service(
    142            &app,
    143            post_request!(&work, VERIFY_CAPTCHA_URL).to_request(),
    144        )
    145        .await;
    146        assert_eq!(pow_verify_resp.status(), StatusCode::OK);
    147        let client_token: ValidationToken = test::read_body_json(pow_verify_resp).await;
    148
    149        let mut validate_payload = VerifyCaptchaResultPayload {
    150            token: client_token.token.clone(),
    151            key: token_key.key.clone(),
    152            secret: NAME.to_string(),
    153        };
    154
    155        // siteverify authentication failure
    156        bad_post_req_test(
    157            data,
    158            NAME,
    159            PASSWORD,
    160            VERIFY_TOKEN_URL,
    161            &validate_payload,
    162            ServiceError::WrongPassword,
    163        )
    164        .await;
    165        //       let validate_client_token = test::call_service(
    166        //            &app,
    167        //            post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
    168        //        )
    169        //        .await;
    170        //        assert_eq!(validate_client_token.status(), StatusCode::OK);
    171        //        let resp: CaptchaValidateResp =
    172        //            test::read_body_json(validate_client_token).await;
    173        //        assert!(resp.valid);
    174
    175        // verifying work
    176        validate_payload.secret = secret.clone();
    177
    178        let validate_client_token = test::call_service(
    179            &app,
    180            post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
    181        )
    182        .await;
    183        assert_eq!(validate_client_token.status(), StatusCode::OK);
    184        let resp: CaptchaValidateResp =
    185            test::read_body_json(validate_client_token).await;
    186        assert!(resp.valid);
    187
    188        // string not found
    189        let string_not_found = test::call_service(
    190            &app,
    191            post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
    192        )
    193        .await;
    194        let resp: CaptchaValidateResp = test::read_body_json(string_not_found).await;
    195        assert!(!resp.valid);
    196    }
    197}