mcaptcha/api/v1/pow/
verify_token.rs1use 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#[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 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 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 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 bad_post_req_test(
157 data,
158 NAME,
159 PASSWORD,
160 VERIFY_TOKEN_URL,
161 &validate_payload,
162 ServiceError::WrongPassword,
163 )
164 .await;
165 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 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}