This commit is contained in:
Aravinth Manivannan 2021-06-09 14:18:31 +05:30
parent 07f124fb82
commit 86b3f2cd95
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
6 changed files with 36 additions and 47 deletions

View File

@ -99,36 +99,6 @@ impl From<MCaptcha> for Counter {
Counter(m) Counter(m)
} }
} }
// impl Counter {
// /// increments the visitor count by one
// pub fn add_visitor(&mut self) {
// self.visitor_threshold += 1;
// if self.visitor_threshold > self.defense.visitor_threshold() {
// self.defense.tighten_up();
// } else {
// self.defense.loosen_up();
// }
// }
//
// /// decrements the visitor count by one
// pub fn decrement_visitor(&mut self) {
// if self.visitor_threshold > 0 {
// self.visitor_threshold -= 1;
// }
// }
//
// /// get current difficulty factor
// pub fn get_difficulty(&self) -> u32 {
// self.defense.get_difficulty()
// }
//
// /// get [Counter]'s lifetime
// pub fn get_duration(&self) -> u64 {
// self.duration
// }
// }
impl Actor for Counter { impl Actor for Counter {
type Context = Context<Self>; type Context = Context<Self>;
} }
@ -151,15 +121,6 @@ impl Handler<DeleteVisitor> for Counter {
#[rtype(result = "AddVisitorResult")] #[rtype(result = "AddVisitorResult")]
pub struct AddVisitor; pub struct AddVisitor;
//impl AddVisitorResult {
// fn new(m: &Counter) -> Self {
// AddVisitorResult {
// duration: m.0.get_duration(),
// difficulty_factor: m.0.get_difficulty(),
// }
// }
//}
impl Handler<AddVisitor> for Counter { impl Handler<AddVisitor> for Counter {
type Result = MessageResult<AddVisitor>; type Result = MessageResult<AddVisitor>;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! [Master] actor module that manages [Counter] actors //! Embedded [Master] actor module that manages [Counter] actors
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::time::Duration; use std::time::Duration;

View File

@ -15,6 +15,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Embedded master actor: manages counter and all mCaptcha stuff in-memory
pub mod counter; pub mod counter;
pub mod master; pub mod master;

View File

@ -16,7 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! [Master] actor module that manages [MCaptcha] actors //! [Master] actor module that manages [MCaptcha] actors
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::mcaptcha::*; use crate::mcaptcha::*;
@ -34,6 +33,8 @@ pub trait Master:
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
/// [mCaptcha Redis module](https://github.com/mCaptcha/cache) uses this datatype for CAPTCHA
/// registration
pub struct CreateMCaptcha { pub struct CreateMCaptcha {
pub levels: Vec<crate::defense::Level>, pub levels: Vec<crate::defense::Level>,
pub duration: u64, pub duration: u64,
@ -49,6 +50,7 @@ pub struct AddVisitorResult {
} }
impl AddVisitorResult { impl AddVisitorResult {
/// create new [AddVisitorResult] from [MCaptcha]
pub fn new(m: &MCaptcha) -> Self { pub fn new(m: &MCaptcha) -> Self {
AddVisitorResult { AddVisitorResult {
duration: m.get_duration(), duration: m.get_duration(),
@ -59,6 +61,7 @@ impl AddVisitorResult {
#[cfg(feature = "full")] #[cfg(feature = "full")]
pub mod messages { pub mod messages {
//! Messages that a [super::Master] should respond to
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use actix::dev::*; use actix::dev::*;

View File

@ -28,7 +28,7 @@ use crate::redis::RedisConnection;
/// Redis instance with mCaptcha Redis module loaded /// Redis instance with mCaptcha Redis module loaded
pub struct MCaptchaRedis(Redis); pub struct MCaptchaRedis(Redis);
/// Redis instance with mCaptcha Redis module loaded /// Connection to Redis instance with mCaptcha Redis module loaded
pub struct MCaptchaRedisConnection(RedisConnection); pub struct MCaptchaRedisConnection(RedisConnection);
const GET: &str = "MCAPTCHA_CACHE.GET"; const GET: &str = "MCAPTCHA_CACHE.GET";
@ -40,6 +40,10 @@ const CAPTCHA_EXISTS: &str = "MCAPTCHA_CACHE.CAPTCHA_EXISTS";
const MODULE_NAME: &str = "mcaptcha_cahce"; const MODULE_NAME: &str = "mcaptcha_cahce";
impl MCaptchaRedis { impl MCaptchaRedis {
/// 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> { pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let redis = Redis::new(redis).await?; let redis = Redis::new(redis).await?;
let m = MCaptchaRedis(redis); let m = MCaptchaRedis(redis);
@ -47,13 +51,16 @@ impl MCaptchaRedis {
Ok(m) Ok(m)
} }
/// Get connection to a Redis instance with mCaptcha Redis module loaded
///
/// Uses interior mutability so look out for panics!
pub fn get_client(&self) -> MCaptchaRedisConnection { pub fn get_client(&self) -> MCaptchaRedisConnection {
MCaptchaRedisConnection(self.0.get_client()) MCaptchaRedisConnection(self.0.get_client())
} }
} }
impl MCaptchaRedisConnection { impl MCaptchaRedisConnection {
pub async fn is_module_loaded(&self) -> CaptchaResult<()> { async fn is_module_loaded(&self) -> CaptchaResult<()> {
let modules: Vec<Vec<String>> = self let modules: Vec<Vec<String>> = self
.0 .0
.exec(redis::cmd("MODULE").arg(&["LIST"])) .exec(redis::cmd("MODULE").arg(&["LIST"]))
@ -94,12 +101,14 @@ impl MCaptchaRedisConnection {
Ok(()) Ok(())
} }
/// Add visitor
pub async fn add_visitor(&self, msg: AddVisitor) -> CaptchaResult<Option<AddVisitorResult>> { pub async fn add_visitor(&self, msg: AddVisitor) -> CaptchaResult<Option<AddVisitorResult>> {
let res: String = self.0.exec(redis::cmd(ADD_VISITOR).arg(&[msg.0])).await?; let res: String = self.0.exec(redis::cmd(ADD_VISITOR).arg(&[msg.0])).await?;
let res: AddVisitorResult = serde_json::from_str(&res).unwrap(); let res: AddVisitorResult = serde_json::from_str(&res).unwrap();
Ok(Some(res)) Ok(Some(res))
} }
/// Register new mCaptcha with Redis
pub async fn add_mcaptcha(&self, msg: AddSite) -> CaptchaResult<()> { pub async fn add_mcaptcha(&self, msg: AddSite) -> CaptchaResult<()> {
let name = msg.id; let name = msg.id;
let captcha: CreateMCaptcha = msg.mcaptcha.into(); let captcha: CreateMCaptcha = msg.mcaptcha.into();
@ -110,6 +119,7 @@ impl MCaptchaRedisConnection {
Ok(()) Ok(())
} }
/// Check if an mCaptcha object is available in Redis
pub async fn check_captcha_exists(&self, captcha: &str) -> CaptchaResult<bool> { pub async fn check_captcha_exists(&self, captcha: &str) -> CaptchaResult<bool> {
let exists: usize = self let exists: usize = self
.0 .0
@ -129,11 +139,13 @@ impl MCaptchaRedisConnection {
} }
} }
/// Delete an mCaptcha object from Redis
pub async fn delete_captcha(&self, captcha: &str) -> CaptchaResult<()> { pub async fn delete_captcha(&self, captcha: &str) -> CaptchaResult<()> {
self.0.exec(redis::cmd(DEL).arg(&[captcha])).await?; self.0.exec(redis::cmd(DEL).arg(&[captcha])).await?;
Ok(()) Ok(())
} }
/// 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?;
Ok(visitors) Ok(visitors)

View File

@ -15,25 +15,28 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
//! Redis Client/Connection manager that can handle both single and clustered Redis Instances
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use redis::cluster::ClusterClient; use redis::cluster::ClusterClient;
use redis::FromRedisValue;
//use redis::cluster::ClusterConnection;
use redis::Client; use redis::Client;
//use redis::Connection; use redis::FromRedisValue;
use redis::{aio::Connection, cluster::ClusterConnection}; use redis::{aio::Connection, cluster::ClusterConnection};
use crate::errors::*; use crate::errors::*;
/// Client configuration
#[derive(Clone)] #[derive(Clone)]
pub enum RedisConfig { pub enum RedisConfig {
/// Redis server URL
Single(String), Single(String),
/// List of URL of Redis nodes in cluster mode
Cluster(Vec<String>), Cluster(Vec<String>),
} }
impl RedisConfig { impl RedisConfig {
/// Create Redis connection
pub fn connect(&self) -> RedisClient { pub fn connect(&self) -> RedisClient {
match self { match self {
Self::Single(url) => { Self::Single(url) => {
@ -48,6 +51,7 @@ impl RedisConfig {
} }
} }
/// Redis connection - manages both single and clustered deployments
pub enum RedisConnection { pub enum RedisConnection {
Single(Rc<RefCell<Connection>>), Single(Rc<RefCell<Connection>>),
Cluster(Rc<RefCell<ClusterConnection>>), Cluster(Rc<RefCell<ClusterConnection>>),
@ -55,6 +59,7 @@ pub enum RedisConnection {
impl RedisConnection { impl RedisConnection {
#[inline] #[inline]
/// Get client. Uses interior mutability, so lookout for panics
pub fn get_client(&self) -> Self { pub fn get_client(&self) -> Self {
match self { match self {
Self::Single(con) => Self::Single(Rc::clone(&con)), Self::Single(con) => Self::Single(Rc::clone(&con)),
@ -62,6 +67,7 @@ impl RedisConnection {
} }
} }
#[inline] #[inline]
/// execute a redis command against a [Self]
pub async fn exec<T: FromRedisValue>(&self, cmd: &mut redis::Cmd) -> redis::RedisResult<T> { pub async fn exec<T: FromRedisValue>(&self, cmd: &mut redis::Cmd) -> redis::RedisResult<T> {
match self { match self {
RedisConnection::Single(con) => cmd.query_async(&mut *con.borrow_mut()).await, RedisConnection::Single(con) => cmd.query_async(&mut *con.borrow_mut()).await,
@ -71,17 +77,21 @@ impl RedisConnection {
} }
#[derive(Clone)] #[derive(Clone)]
/// Client Configuration that can be used to get new connection shuld [RedisConnection] fail
pub enum RedisClient { pub enum RedisClient {
Single(Client), Single(Client),
Cluster(ClusterClient), Cluster(ClusterClient),
} }
/// A Redis Client Object that encapsulates [RedisClient] and [RedisConnection].
/// Use this when you need a Redis Client
pub struct Redis { pub struct Redis {
_client: RedisClient, _client: RedisClient,
connection: RedisConnection, connection: RedisConnection,
} }
impl Redis { impl Redis {
/// create new [Redis]. Will try to connect to Redis instance specified in [RedisConfig]
pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> { pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let (_client, connection) = Self::connect(redis).await; let (_client, connection) = Self::connect(redis).await;
let master = Self { let master = Self {
@ -91,6 +101,9 @@ impl Redis {
Ok(master) Ok(master)
} }
/// Get client to do interact with Redis server.
///
/// Uses Interior mutability so look out for panics
pub fn get_client(&self) -> RedisConnection { pub fn get_client(&self) -> RedisConnection {
self.connection.get_client() self.connection.get_client()
} }