diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 710c67c..92612cf 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -21,10 +21,12 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "full")] pub mod hashcache; +//#[cfg(feature = "full")] +//pub mod redis; #[derive(Serialize, Deserialize)] pub struct AddChallenge { - pub difficulty: usize, + pub difficulty: u32, pub duration: u64, pub challenge: String, } diff --git a/src/cache/redis.rs b/src/cache/redis.rs index ae40835..5273550 100644 --- a/src/cache/redis.rs +++ b/src/cache/redis.rs @@ -16,16 +16,18 @@ * along with this program. If not, see . */ //! Cache implementation that uses Redis -use crate::redis::Redis; +use crate::redis::mcaptcha_redis::MCaptchaRedis; +use crate::redis::RedisConfig; use std::collections::HashMap; use actix::prelude::*; use super::messages::*; +use super::AddChallenge; use super::Save; use crate::errors::*; -pub struct RedisCache(Redis); +pub struct RedisCache(MCaptchaRedis); #[derive(Clone, Default)] /// cache datastructure implementing [Save] @@ -35,15 +37,26 @@ pub struct HashCache { } 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 { + let m = MCaptchaRedis::new(redis).await?; + Ok(Self(m)) + } + // save [PoWConfig] to cache async fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> { let challenge = config.string; - let config: CachedPoWConfig = CachedPoWConfig { - key: config.key, - difficulty_factor: config.difficulty_factor, + let payload: AddChallenge = AddChallenge { + challenge: config.string, + difficulty: config.difficulty_factor as usize, duration: config.duration, }; + let payload = serde_json::to_string(&payload).unwrap(); + // {MCAPTCHA_NAME}:difficulty_map:challenge (difficulty_factor -> duration) EX duration // TODO use hashmap diff --git a/src/master/redis/master.rs b/src/master/redis/master.rs index 9d1df46..7de8f41 100644 --- a/src/master/redis/master.rs +++ b/src/master/redis/master.rs @@ -19,10 +19,10 @@ use std::sync::mpsc; use actix::dev::*; -use super::connection::MCaptchaRedis; use crate::errors::*; use crate::master::messages::{AddSite, AddVisitor}; use crate::master::Master as MasterTrait; +use crate::redis::mcaptcha_redis::MCaptchaRedis; use crate::redis::RedisConfig; pub struct Master { diff --git a/src/master/redis/mod.rs b/src/master/redis/mod.rs index fbed376..09f3c9a 100644 --- a/src/master/redis/mod.rs +++ b/src/master/redis/mod.rs @@ -16,5 +16,4 @@ * along with this program. If not, see . */ -pub mod connection; pub mod master; diff --git a/src/master/redis/connection.rs b/src/redis/mcaptcha_redis.rs similarity index 74% rename from src/master/redis/connection.rs rename to src/redis/mcaptcha_redis.rs index ab57d55..d6f5f99 100644 --- a/src/master/redis/connection.rs +++ b/src/redis/mcaptcha_redis.rs @@ -17,6 +17,8 @@ */ use redis::Value; +use crate::cache::messages::VerifyCaptchaResult; +use crate::cache::AddChallenge; use crate::errors::*; use crate::master::messages::{AddSite, AddVisitor}; use crate::master::AddVisitorResult; @@ -36,6 +38,8 @@ const ADD_VISITOR: &str = "MCAPTCHA_CACHE.ADD_VISITOR"; const DEL: &str = "MCAPTCHA_CACHE.DELETE_CAPTCHA"; const ADD_CAPTCHA: &str = "MCAPTCHA_CACHE.ADD_CAPTCHA"; 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"; @@ -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() { if let Value::Bulk(mut val) = self @@ -138,6 +150,31 @@ impl MCaptchaRedisConnection { 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 { + 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 pub async fn get_visitors(&self, captcha: &str) -> CaptchaResult { 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 REDIS_URL: &str = "redis://127.0.1.1/"; + const CHALLENGE: &str = "randomchallengestring"; #[actix_rt::test] async fn redis_master_works() { @@ -165,11 +203,15 @@ pub mod tests { { 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.check_captcha_exists(CAPTCHA_NAME).await.unwrap()); let add_mcaptcha_msg = AddSite { id: CAPTCHA_NAME.into(), - mcaptcha: get_mcaptcha(), + mcaptcha, }; assert!(r.add_mcaptcha(add_mcaptcha_msg).await.is_ok()); @@ -181,10 +223,32 @@ pub mod tests { assert_eq!(visitors, 1); 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(); 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()); } } diff --git a/src/redis.rs b/src/redis/mod.rs similarity index 99% rename from src/redis.rs rename to src/redis/mod.rs index 7f3e971..4f0bee3 100644 --- a/src/redis.rs +++ b/src/redis/mod.rs @@ -24,6 +24,7 @@ use redis::Client; use redis::FromRedisValue; use redis::{aio::Connection, cluster::ClusterConnection}; +pub mod mcaptcha_redis; use crate::errors::*; /// Client configuration