pow verification

This commit is contained in:
realaravinth 2021-04-10 20:19:59 +05:30
parent e76dd8014c
commit 7777db477e
No known key found for this signature in database
GPG key ID: AD9F0F08E855ED88
9 changed files with 173 additions and 27 deletions

14
Cargo.lock generated
View file

@ -521,11 +521,10 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bincode"
version = "1.3.2"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d175dfa69e619905c4c3cdb7c3c203fa3bdd5d51184e3afdb2742c0280493772"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"byteorder",
"serde 1.0.125",
]
@ -601,9 +600,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
[[package]]
name = "byteorder"
version = "1.3.4"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
@ -629,7 +628,7 @@ dependencies = [
[[package]]
name = "cache-buster"
version = "0.1.0"
source = "git+https://github.com/realaravinth/cache-buster#26d40b9993820837aa9d2d683937e502bfd3be12"
source = "git+https://github.com/realaravinth/cache-buster#71d5ef67a2788789922eaa484e10269acbaeb8a7"
dependencies = [
"data-encoding",
"derive_builder 0.10.0",
@ -1295,6 +1294,7 @@ dependencies = [
"m_captcha",
"mime",
"mime_guess",
"pow_sha256",
"pretty_env_logger",
"rand 0.8.3",
"rust-embed",
@ -1611,7 +1611,7 @@ dependencies = [
[[package]]
name = "m_captcha"
version = "0.1.3"
source = "git+https://github.com/mCaptcha/mCaptcha?branch=master#2d120d6791d8a32e7b3e31a7c5f1b54e3b35f9ef"
source = "git+https://github.com/mCaptcha/mCaptcha?branch=master#29cd8f4fd83a3646a48ca2c9f5563d8d5360d2c3"
dependencies = [
"actix",
"derive_builder 0.9.0",

View file

@ -67,3 +67,6 @@ serde_json = "1"
yaml-rust = "0.4.5"
cache-buster = { version = "0.1", git = "https://github.com/realaravinth/cache-buster" }
mime = "0.3.16"
[dev-dependencies]
pow_sha256 = { version = "0.2.1", git = "https://github.com/mcaptcha/pow_sha256" }

View file

@ -55,6 +55,5 @@ fn cache_bust() {
.build()
.unwrap();
config.init().unwrap();
config.hash().unwrap().to_env();
config.process().unwrap().to_env();
}

View file

@ -16,14 +16,12 @@
*/
use actix::prelude::*;
use actix_identity::Identity;
use actix_web::{post, web, HttpResponse, Responder};
use m_captcha::{defense::LevelBuilder, master::AddSiteBuilder, DefenseBuilder, MCaptchaBuilder};
use serde::{Deserialize, Serialize};
use super::duration::GetDurationResp;
use super::is_authenticated;
use super::levels::I32Levels;
use super::GetDurationResp;
use super::I32Levels;
use crate::errors::*;
use crate::Data;
@ -44,10 +42,7 @@ pub struct GetConfigPayload {
pub async fn get_config(
payload: web::Json<GetConfigPayload>,
data: web::Data<Data>,
id: Identity,
) -> ServiceResult<impl Responder> {
is_authenticated(&id)?;
let res = sqlx::query!(
"SELECT EXISTS (SELECT 1 from mcaptcha_config WHERE key = $1)",
&payload.key,

View file

@ -0,0 +1,23 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pub mod get_config;
pub mod verify_pow;
pub use super::duration::GetDurationResp;
pub use super::is_authenticated;
pub use super::levels::I32Levels;

View file

View file

@ -0,0 +1,135 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use actix_web::{post, web, HttpResponse, Responder};
use m_captcha::pow::Work;
use serde::{Deserialize, Serialize};
use crate::errors::*;
use crate::Data;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PoWConfig {
pub name: String,
pub domain: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ValidationToken {
pub token: String,
}
// API keys are mcaptcha actor names
#[post("/api/v1/mcaptcha/pow/verify")]
pub async fn verify_pow(
payload: web::Json<Work>,
data: web::Data<Data>,
) -> ServiceResult<impl Responder> {
let res = data.captcha.verify_pow(payload.into_inner()).await?;
let payload = ValidationToken { token: res };
Ok(HttpResponse::Ok().json(payload))
}
#[cfg(test)]
mod tests {
use actix_web::http::{header, StatusCode};
use actix_web::test;
use m_captcha::pow::PoWConfig;
use super::*;
use crate::api::v1::mcaptcha::pow::get_config::GetConfigPayload;
use crate::api::v1::services as v1_services;
use crate::tests::*;
use crate::*;
#[actix_rt::test]
async fn verify_pow_works() {
const NAME: &str = "powverifyusr";
const PASSWORD: &str = "testingpas";
const EMAIL: &str = "verifyuser@a.com";
const VERIFY_URL: &str = "/api/v1/mcaptcha/pow/verify";
const GET_URL: &str = "/api/v1/mcaptcha/pow/config";
// const UPDATE_URL: &str = "/api/v1/mcaptcha/domain/token/duration/update";
{
let data = Data::new().await;
delete_user(NAME, &data).await;
}
register_and_signin(NAME, EMAIL, PASSWORD).await;
let (data, _, _signin_resp, token_key) = add_levels_util(NAME, PASSWORD).await;
let mut app = get_app!(data).await;
let get_config_payload = GetConfigPayload {
key: token_key.key.clone(),
};
// update and check changes
let get_config_resp = test::call_service(
&mut app,
post_request!(&get_config_payload, GET_URL).to_request(),
)
.await;
assert_eq!(get_config_resp.status(), StatusCode::OK);
let config: PoWConfig = test::read_body_json(get_config_resp).await;
let pow = pow_sha256::ConfigBuilder::default()
.salt(config.salt)
.build()
.unwrap();
let work = pow
.prove_work(&config.string.clone(), config.difficulty_factor)
.unwrap();
let work = Work {
string: config.string.clone(),
result: work.result,
nonce: work.nonce,
key: token_key.key.clone(),
};
let pow_verify_resp =
test::call_service(&mut app, post_request!(&work, VERIFY_URL).to_request()).await;
assert_eq!(pow_verify_resp.status(), StatusCode::OK);
let string_not_found =
test::call_service(&mut app, post_request!(&work, VERIFY_URL).to_request()).await;
assert_eq!(string_not_found.status(), StatusCode::BAD_REQUEST);
let err: ErrorToResponse = test::read_body_json(string_not_found).await;
assert_eq!(
err.error,
format!(
"{}",
ServiceError::CaptchaError(m_captcha::errors::CaptchaError::StringNotFound)
)
);
let pow_config_resp = test::call_service(
&mut app,
post_request!(&get_config_payload, GET_URL).to_request(),
)
.await;
assert_eq!(pow_config_resp.status(), StatusCode::OK);
// I'm not checking for errors because changing work.result triggered
// InssuficientDifficulty, which is possible becuase m_captcha calculates
// difficulty with the submitted result. Besides, this endpoint is merely
// propagating errors from m_captcha and m_captcha has tests covering the
// pow aspects ¯\_(ツ)_/¯
}
}

View file

@ -51,7 +51,8 @@ pub fn services(cfg: &mut ServiceConfig) {
cfg.service(mcaptcha::duration::get_duration);
// pow
cfg.service(mcaptcha::pow::get_config);
cfg.service(mcaptcha::pow::get_config::get_config);
cfg.service(mcaptcha::pow::verify_pow::verify_pow);
}
#[cfg(test)]

View file

@ -76,9 +76,6 @@ pub enum ServiceError {
/// when the a token name is already taken
#[display(fmt = "token name not available")]
TokenNameTaken,
/// when the a host name is already taken
#[display(fmt = "host name not available")]
HostnameTaken,
/// token not found
#[display(fmt = "Token not found. Is token registered?")]
TokenNotFound,
@ -88,10 +85,6 @@ pub enum ServiceError {
#[display(fmt = "Couldn't reach your server. If Problem presists, contact support")]
ClientServerUnreachable,
#[display(fmt = "Couldn't parse challenge from your server. Check for courruption")]
ChallengeCourruption,
#[display(fmt = "Verification failure, vaules didn't match")]
ChallengeVerificationFailure,
}
#[derive(Serialize, Deserialize)]
@ -132,10 +125,7 @@ impl ResponseError for ServiceError {
ServiceError::TokenNameTaken => StatusCode::BAD_REQUEST,
ServiceError::TokenNotFound => StatusCode::NOT_FOUND,
ServiceError::HostnameTaken => StatusCode::BAD_REQUEST,
ServiceError::ClientServerUnreachable => StatusCode::SERVICE_UNAVAILABLE,
ServiceError::ChallengeCourruption => StatusCode::BAD_REQUEST,
ServiceError::ChallengeVerificationFailure => StatusCode::UNAUTHORIZED,
ServiceError::CaptchaError(e) => match e {
CaptchaError::MailboxError => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::BAD_REQUEST,