redis: add and get challenge

This commit is contained in:
Aravinth Manivannan 2021-06-09 21:00:29 +05:30
parent 0ff9cf9cb0
commit a98658c57d
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
6 changed files with 90 additions and 11 deletions

4
src/cache/mod.rs vendored
View File

@ -21,10 +21,12 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod hashcache; pub mod hashcache;
//#[cfg(feature = "full")]
//pub mod redis;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct AddChallenge { pub struct AddChallenge {
pub difficulty: usize, pub difficulty: u32,
pub duration: u64, pub duration: u64,
pub challenge: String, pub challenge: String,
} }

23
src/cache/redis.rs vendored
View File

@ -16,16 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Cache implementation that uses Redis //! Cache implementation that uses Redis
use crate::redis::Redis; use crate::redis::mcaptcha_redis::MCaptchaRedis;
use crate::redis::RedisConfig;
use std::collections::HashMap; use std::collections::HashMap;
use actix::prelude::*; use actix::prelude::*;
use super::messages::*; use super::messages::*;
use super::AddChallenge;
use super::Save; use super::Save;
use crate::errors::*; use crate::errors::*;
pub struct RedisCache(Redis); pub struct RedisCache(MCaptchaRedis);
#[derive(Clone, Default)] #[derive(Clone, Default)]
/// cache datastructure implementing [Save] /// cache datastructure implementing [Save]
@ -35,15 +37,26 @@ pub struct HashCache {
} }
impl RedisCache { impl RedisCache {
/// Get new [MCaptchaRedis]. Use this when executing commands that are
/// only supported by mCaptcha Redis module. Internally, when object
/// is created, checks are performed to check if the module is loaded and if
/// the required commands are available
pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let m = MCaptchaRedis::new(redis).await?;
Ok(Self(m))
}
// save [PoWConfig] to cache // save [PoWConfig] to cache
async fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> { async fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> {
let challenge = config.string; let challenge = config.string;
let config: CachedPoWConfig = CachedPoWConfig { let payload: AddChallenge = AddChallenge {
key: config.key, challenge: config.string,
difficulty_factor: config.difficulty_factor, difficulty: config.difficulty_factor as usize,
duration: config.duration, duration: config.duration,
}; };
let payload = serde_json::to_string(&payload).unwrap();
// {MCAPTCHA_NAME}:difficulty_map:challenge (difficulty_factor -> duration) EX duration // {MCAPTCHA_NAME}:difficulty_map:challenge (difficulty_factor -> duration) EX duration
// TODO use hashmap // TODO use hashmap

View File

@ -19,10 +19,10 @@ use std::sync::mpsc;
use actix::dev::*; use actix::dev::*;
use super::connection::MCaptchaRedis;
use crate::errors::*; use crate::errors::*;
use crate::master::messages::{AddSite, AddVisitor}; use crate::master::messages::{AddSite, AddVisitor};
use crate::master::Master as MasterTrait; use crate::master::Master as MasterTrait;
use crate::redis::mcaptcha_redis::MCaptchaRedis;
use crate::redis::RedisConfig; use crate::redis::RedisConfig;
pub struct Master { pub struct Master {

View File

@ -16,5 +16,4 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
pub mod connection;
pub mod master; pub mod master;

View File

@ -17,6 +17,8 @@
*/ */
use redis::Value; use redis::Value;
use crate::cache::messages::VerifyCaptchaResult;
use crate::cache::AddChallenge;
use crate::errors::*; use crate::errors::*;
use crate::master::messages::{AddSite, AddVisitor}; use crate::master::messages::{AddSite, AddVisitor};
use crate::master::AddVisitorResult; use crate::master::AddVisitorResult;
@ -36,6 +38,8 @@ const ADD_VISITOR: &str = "MCAPTCHA_CACHE.ADD_VISITOR";
const DEL: &str = "MCAPTCHA_CACHE.DELETE_CAPTCHA"; const DEL: &str = "MCAPTCHA_CACHE.DELETE_CAPTCHA";
const ADD_CAPTCHA: &str = "MCAPTCHA_CACHE.ADD_CAPTCHA"; const ADD_CAPTCHA: &str = "MCAPTCHA_CACHE.ADD_CAPTCHA";
const CAPTCHA_EXISTS: &str = "MCAPTCHA_CACHE.CAPTCHA_EXISTS"; const CAPTCHA_EXISTS: &str = "MCAPTCHA_CACHE.CAPTCHA_EXISTS";
const ADD_CHALLENGE: &str = "MCAPTCHA_CACHE.ADD_CHALLENGE";
const GET_CHALLENGE: &str = "MCAPTCHA_CACHE.GET_CHALLENGE";
const MODULE_NAME: &str = "mcaptcha_cahce"; const MODULE_NAME: &str = "mcaptcha_cahce";
@ -74,7 +78,15 @@ impl MCaptchaRedisConnection {
} }
} }
let commands = vec![ADD_VISITOR, ADD_CAPTCHA, DEL, CAPTCHA_EXISTS, GET]; let commands = vec![
ADD_VISITOR,
ADD_CAPTCHA,
DEL,
CAPTCHA_EXISTS,
GET,
ADD_CHALLENGE,
GET_CHALLENGE,
];
for cmd in commands.iter() { for cmd in commands.iter() {
if let Value::Bulk(mut val) = self if let Value::Bulk(mut val) = self
@ -138,6 +150,31 @@ impl MCaptchaRedisConnection {
Ok(()) Ok(())
} }
/// Delete an mCaptcha object from Redis
pub async fn add_challenge(
&self,
captcha: &str,
challlenge: &AddChallenge,
) -> CaptchaResult<()> {
let payload = serde_json::to_string(challlenge).unwrap();
self.0
.exec(redis::cmd(ADD_CHALLENGE).arg(&[captcha, &payload]))
.await?;
Ok(())
}
/// Delete an mCaptcha object from Redis
pub async fn get_challenge(
&self,
msg: &VerifyCaptchaResult,
) -> CaptchaResult<AddVisitorResult> {
let challege: String = self
.0
.exec(redis::cmd(GET_CHALLENGE).arg(&[&msg.key, &msg.token]))
.await?;
Ok(serde_json::from_str(&challege).unwrap())
}
/// Get number of visitors of an mCaptcha object from Redis /// Get number of visitors of an mCaptcha object from Redis
pub async fn get_visitors(&self, captcha: &str) -> CaptchaResult<usize> { pub async fn get_visitors(&self, captcha: &str) -> CaptchaResult<usize> {
let visitors: usize = self.0.exec(redis::cmd(GET).arg(&[captcha])).await?; let visitors: usize = self.0.exec(redis::cmd(GET).arg(&[captcha])).await?;
@ -153,6 +190,7 @@ pub mod tests {
const CAPTCHA_NAME: &str = "REDIS_CAPTCHA_TEST"; const CAPTCHA_NAME: &str = "REDIS_CAPTCHA_TEST";
const REDIS_URL: &str = "redis://127.0.1.1/"; const REDIS_URL: &str = "redis://127.0.1.1/";
const CHALLENGE: &str = "randomchallengestring";
#[actix_rt::test] #[actix_rt::test]
async fn redis_master_works() { async fn redis_master_works() {
@ -165,11 +203,15 @@ pub mod tests {
{ {
let _ = r.delete_captcha(CAPTCHA_NAME).await; let _ = r.delete_captcha(CAPTCHA_NAME).await;
} }
let mcaptcha = get_mcaptcha();
// let duration = mcaptcha.get_duration();
assert!(r.is_module_loaded().await.is_ok()); assert!(r.is_module_loaded().await.is_ok());
assert!(!r.check_captcha_exists(CAPTCHA_NAME).await.unwrap()); assert!(!r.check_captcha_exists(CAPTCHA_NAME).await.unwrap());
let add_mcaptcha_msg = AddSite { let add_mcaptcha_msg = AddSite {
id: CAPTCHA_NAME.into(), id: CAPTCHA_NAME.into(),
mcaptcha: get_mcaptcha(), mcaptcha,
}; };
assert!(r.add_mcaptcha(add_mcaptcha_msg).await.is_ok()); assert!(r.add_mcaptcha(add_mcaptcha_msg).await.is_ok());
@ -181,10 +223,32 @@ pub mod tests {
assert_eq!(visitors, 1); assert_eq!(visitors, 1);
let add_visitor_msg = AddVisitor(CAPTCHA_NAME.into()); let add_visitor_msg = AddVisitor(CAPTCHA_NAME.into());
assert!(r.add_visitor(add_visitor_msg).await.is_ok()); let resp = r.add_visitor(add_visitor_msg.clone()).await;
assert!(resp.is_ok());
assert!(resp.unwrap().is_some());
let visitors = r.get_visitors(CAPTCHA_NAME).await.unwrap(); let visitors = r.get_visitors(CAPTCHA_NAME).await.unwrap();
assert_eq!(visitors, 2); assert_eq!(visitors, 2);
let add_visitor_res = r.add_visitor(add_visitor_msg).await.unwrap().unwrap();
let add_challenge_msg = AddChallenge {
difficulty: add_visitor_res.difficulty_factor,
duration: add_visitor_res.duration,
challenge: CHALLENGE.into(),
};
assert!(r
.add_challenge(CAPTCHA_NAME, &add_challenge_msg)
.await
.is_ok());
let verify_msg = VerifyCaptchaResult {
token: CHALLENGE.into(),
key: CAPTCHA_NAME.into(),
};
let x = r.get_challenge(&verify_msg).await.unwrap();
assert_eq!(x.duration, add_challenge_msg.duration);
assert_eq!(x.difficulty_factor, add_challenge_msg.difficulty);
assert!(r.delete_captcha(CAPTCHA_NAME).await.is_ok()); assert!(r.delete_captcha(CAPTCHA_NAME).await.is_ok());
} }
} }

View File

@ -24,6 +24,7 @@ use redis::Client;
use redis::FromRedisValue; use redis::FromRedisValue;
use redis::{aio::Connection, cluster::ClusterConnection}; use redis::{aio::Connection, cluster::ClusterConnection};
pub mod mcaptcha_redis;
use crate::errors::*; use crate::errors::*;
/// Client configuration /// Client configuration