moved mcaptcha into embedded
This commit is contained in:
parent
e7dd8bff63
commit
cc88c69fc6
8 changed files with 168 additions and 122 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,9 @@
|
||||||
## 0.1.4
|
## 0.1.4
|
||||||
|
|
||||||
|
## Added:
|
||||||
|
|
||||||
|
- `Master` trait: provides methods to manage mcaptcha
|
||||||
|
|
||||||
## Changed:
|
## Changed:
|
||||||
|
|
||||||
- `PoWConfig` has an extra field to send internal `PoW` salt to clients.
|
- `PoWConfig` has an extra field to send internal `PoW` salt to clients.
|
||||||
|
@ -11,9 +15,14 @@
|
||||||
- `master::Master` is moved to `master::embedded::master` in preparation
|
- `master::Master` is moved to `master::embedded::master` in preparation
|
||||||
for Redis based implementation.
|
for Redis based implementation.
|
||||||
|
|
||||||
- `crate::mcaptcha` is moved to `master::embedded::mcaptcha` in preparation
|
- `crate::mcaptcha` is moved to `master::embedded::counter` in preparation
|
||||||
for Redis based implementation.
|
for Redis based implementation.
|
||||||
|
|
||||||
|
- `AddSite` message for `Master` now requires an instance of
|
||||||
|
`crate::master::MCaptcha`. In the case of
|
||||||
|
`crate::master::embedded::master`, it automatically starts `Counter`
|
||||||
|
actor.
|
||||||
|
|
||||||
## 0.1.3
|
## 0.1.3
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use libmcaptcha::{
|
use libmcaptcha::{
|
||||||
cache::{messages::VerifyCaptchaResult, HashCache},
|
cache::{messages::VerifyCaptchaResult, HashCache},
|
||||||
master::embedded::master::{AddSiteBuilder, Master},
|
master::embedded::master::Master,
|
||||||
|
master::AddSiteBuilder,
|
||||||
pow::{ConfigBuilder, Work},
|
pow::{ConfigBuilder, Work},
|
||||||
system::SystemBuilder,
|
system::SystemBuilder,
|
||||||
DefenseBuilder, LevelBuilder, MCaptchaBuilder,
|
DefenseBuilder, LevelBuilder, MCaptchaBuilder,
|
||||||
|
@ -75,8 +76,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
.duration(30)
|
.duration(30)
|
||||||
// .cache(cache)
|
// .cache(cache)
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.start();
|
|
||||||
|
|
||||||
// unique value identifying an MCaptcha actor
|
// unique value identifying an MCaptcha actor
|
||||||
let mcaptcha_name = "batsense.net";
|
let mcaptcha_name = "batsense.net";
|
||||||
|
@ -84,7 +84,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
// add MCaptcha to Master
|
// add MCaptcha to Master
|
||||||
let msg = AddSiteBuilder::default()
|
let msg = AddSiteBuilder::default()
|
||||||
.id(mcaptcha_name.into())
|
.id(mcaptcha_name.into())
|
||||||
.addr(mcaptcha.clone())
|
.mcaptcha(mcaptcha)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
system.master.send(msg).await.unwrap();
|
system.master.send(msg).await.unwrap();
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -48,7 +48,8 @@
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use libmcaptcha::{
|
//! use libmcaptcha::{
|
||||||
//! cache::{messages::VerifyCaptchaResult, HashCache},
|
//! cache::{messages::VerifyCaptchaResult, HashCache},
|
||||||
//! master::embedded::master::{AddSiteBuilder, Master},
|
//! master::embedded::master:: Master,
|
||||||
|
//! master::AddSiteBuilder,
|
||||||
//! pow::{ConfigBuilder, Work},
|
//! pow::{ConfigBuilder, Work},
|
||||||
//! system::SystemBuilder,
|
//! system::SystemBuilder,
|
||||||
//! DefenseBuilder, LevelBuilder, MCaptchaBuilder,
|
//! DefenseBuilder, LevelBuilder, MCaptchaBuilder,
|
||||||
|
@ -123,8 +124,7 @@
|
||||||
//! .duration(30)
|
//! .duration(30)
|
||||||
//! // .cache(cache)
|
//! // .cache(cache)
|
||||||
//! .build()
|
//! .build()
|
||||||
//! .unwrap()
|
//! .unwrap();
|
||||||
//! .start();
|
|
||||||
//!
|
//!
|
||||||
//! // unique value identifying an MCaptcha actor
|
//! // unique value identifying an MCaptcha actor
|
||||||
//! let mcaptcha_name = "batsense.net";
|
//! let mcaptcha_name = "batsense.net";
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
//! // add MCaptcha to Master
|
//! // add MCaptcha to Master
|
||||||
//! let msg = AddSiteBuilder::default()
|
//! let msg = AddSiteBuilder::default()
|
||||||
//! .id(mcaptcha_name.into())
|
//! .id(mcaptcha_name.into())
|
||||||
//! .addr(mcaptcha.clone())
|
//! .mcaptcha(mcaptcha)
|
||||||
//! .build()
|
//! .build()
|
||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//! system.master.send(msg).await.unwrap();
|
//! system.master.send(msg).await.unwrap();
|
||||||
|
@ -196,4 +196,5 @@ mod utils;
|
||||||
pub use crate::cache::hashcache::HashCache;
|
pub use crate::cache::hashcache::HashCache;
|
||||||
|
|
||||||
pub use defense::{Defense, DefenseBuilder, LevelBuilder};
|
pub use defense::{Defense, DefenseBuilder, LevelBuilder};
|
||||||
pub use master::embedded::mcaptcha::{MCaptcha, MCaptchaBuilder};
|
pub use master::embedded::counter::Counter;
|
||||||
|
pub use master::MCaptchaBuilder;
|
||||||
|
|
|
@ -15,11 +15,17 @@
|
||||||
* 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/>.
|
||||||
*/
|
*/
|
||||||
//! MCaptcha actor module that manages defense levels
|
//! Counter actor module that manages defense levels
|
||||||
//!
|
//!
|
||||||
//! ## Usage:
|
//! ## Usage:
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use libmcaptcha::{master::embedded::mcaptcha::AddVisitor, MCaptchaBuilder, cache::HashCache, LevelBuilder, DefenseBuilder};
|
//! use libmcaptcha::{
|
||||||
|
//! master::embedded::counter::{Counter, AddVisitor},
|
||||||
|
//! master::MCaptchaBuilder,
|
||||||
|
//! cache::HashCache,
|
||||||
|
//! LevelBuilder,
|
||||||
|
//! DefenseBuilder
|
||||||
|
//! };
|
||||||
//! // traits from actix needs to be in scope for starting actor
|
//! // traits from actix needs to be in scope for starting actor
|
||||||
//! use actix::prelude::*;
|
//! use actix::prelude::*;
|
||||||
//!
|
//!
|
||||||
|
@ -55,18 +61,20 @@
|
||||||
//! .build()
|
//! .build()
|
||||||
//! .unwrap();
|
//! .unwrap();
|
||||||
//!
|
//!
|
||||||
//! // create and start MCaptcha actor
|
//! // create and start Counter actor
|
||||||
//! //let cache = HashCache::default().start();
|
//! //let cache = HashCache::default().start();
|
||||||
//! let mcaptcha = MCaptchaBuilder::default()
|
//! let mcaptcha = MCaptchaBuilder::default()
|
||||||
//! .defense(defense)
|
//! .defense(defense)
|
||||||
//! // leaky bucket algorithm's emission interval
|
//! // leaky bucket algorithm's emission interval
|
||||||
//! .duration(30)
|
//! .duration(30)
|
||||||
//! .build()
|
//! .build()
|
||||||
//! .unwrap()
|
//! .unwrap();
|
||||||
//! .start();
|
//!
|
||||||
|
//! let counter: Counter = mcaptcha.into();
|
||||||
|
//! let counter = counter.start();
|
||||||
//!
|
//!
|
||||||
//! // increment count when user visits protected routes
|
//! // increment count when user visits protected routes
|
||||||
//! mcaptcha.send(AddVisitor).await.unwrap();
|
//! counter.send(AddVisitor).await.unwrap();
|
||||||
//!
|
//!
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
|
@ -78,72 +86,29 @@ use std::time::Duration;
|
||||||
use actix::dev::*;
|
use actix::dev::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::master::MCaptcha;
|
||||||
defense::Defense,
|
use crate::{defense::Defense, master::AddVisitorResult};
|
||||||
errors::{CaptchaError, CaptchaResult},
|
|
||||||
master::AddVisitorResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Builder for [MCaptcha]
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct MCaptchaBuilder {
|
|
||||||
visitor_threshold: u32,
|
|
||||||
defense: Option<Defense>,
|
|
||||||
duration: Option<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MCaptchaBuilder {
|
|
||||||
fn default() -> Self {
|
|
||||||
MCaptchaBuilder {
|
|
||||||
visitor_threshold: 0,
|
|
||||||
defense: None,
|
|
||||||
duration: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This struct represents the mCaptcha state and is used
|
/// This struct represents the mCaptcha state and is used
|
||||||
/// to configure leaky-bucket lifetime and manage defense
|
/// to configure leaky-bucket lifetime and manage defense
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
pub struct MCaptcha {
|
pub struct Counter {
|
||||||
visitor_threshold: u32,
|
visitor_threshold: u32,
|
||||||
defense: Defense,
|
defense: Defense,
|
||||||
duration: u64,
|
duration: u64,
|
||||||
}
|
}
|
||||||
|
impl From<MCaptcha> for Counter {
|
||||||
impl MCaptchaBuilder {
|
fn from(m: MCaptcha) -> Counter {
|
||||||
/// set defense
|
let m = Counter {
|
||||||
pub fn defense(&mut self, d: Defense) -> &mut Self {
|
duration: m.duration,
|
||||||
self.defense = Some(d);
|
defense: m.defense,
|
||||||
self
|
visitor_threshold: m.visitor_threshold,
|
||||||
}
|
|
||||||
|
|
||||||
/// set duration
|
|
||||||
pub fn duration(&mut self, d: u64) -> &mut Self {
|
|
||||||
self.duration = Some(d);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds new [MCaptcha]
|
|
||||||
pub fn build(&mut self) -> CaptchaResult<MCaptcha> {
|
|
||||||
if self.duration.is_none() {
|
|
||||||
Err(CaptchaError::PleaseSetValue("duration".into()))
|
|
||||||
} else if self.defense.is_none() {
|
|
||||||
Err(CaptchaError::PleaseSetValue("defense".into()))
|
|
||||||
} else if self.duration <= Some(0) {
|
|
||||||
Err(CaptchaError::CaptchaDurationZero)
|
|
||||||
} else {
|
|
||||||
let m = MCaptcha {
|
|
||||||
duration: self.duration.unwrap(),
|
|
||||||
defense: self.defense.clone().unwrap(),
|
|
||||||
visitor_threshold: self.visitor_threshold,
|
|
||||||
};
|
};
|
||||||
Ok(m)
|
m
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MCaptcha {
|
impl Counter {
|
||||||
/// increments the visitor count by one
|
/// increments the visitor count by one
|
||||||
pub fn add_visitor(&mut self) {
|
pub fn add_visitor(&mut self) {
|
||||||
self.visitor_threshold += 1;
|
self.visitor_threshold += 1;
|
||||||
|
@ -166,12 +131,12 @@ impl MCaptcha {
|
||||||
self.defense.get_difficulty()
|
self.defense.get_difficulty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get [MCaptcha]'s lifetime
|
/// get [Counter]'s lifetime
|
||||||
pub fn get_duration(&self) -> u64 {
|
pub fn get_duration(&self) -> u64 {
|
||||||
self.duration
|
self.duration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Actor for MCaptcha {
|
impl Actor for Counter {
|
||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +145,7 @@ impl Actor for MCaptcha {
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
struct DeleteVisitor;
|
struct DeleteVisitor;
|
||||||
|
|
||||||
impl Handler<DeleteVisitor> for MCaptcha {
|
impl Handler<DeleteVisitor> for Counter {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
fn handle(&mut self, _msg: DeleteVisitor, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _msg: DeleteVisitor, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
self.decrement_visitor();
|
self.decrement_visitor();
|
||||||
|
@ -194,7 +159,7 @@ impl Handler<DeleteVisitor> for MCaptcha {
|
||||||
pub struct AddVisitor;
|
pub struct AddVisitor;
|
||||||
|
|
||||||
impl AddVisitorResult {
|
impl AddVisitorResult {
|
||||||
fn new(m: &MCaptcha) -> Self {
|
fn new(m: &Counter) -> Self {
|
||||||
AddVisitorResult {
|
AddVisitorResult {
|
||||||
duration: m.get_duration(),
|
duration: m.get_duration(),
|
||||||
difficulty_factor: m.get_difficulty(),
|
difficulty_factor: m.get_difficulty(),
|
||||||
|
@ -202,7 +167,7 @@ impl AddVisitorResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler<AddVisitor> for MCaptcha {
|
impl Handler<AddVisitor> for Counter {
|
||||||
type Result = MessageResult<AddVisitor>;
|
type Result = MessageResult<AddVisitor>;
|
||||||
|
|
||||||
fn handle(&mut self, _: AddVisitor, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _: AddVisitor, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
@ -228,7 +193,7 @@ impl Handler<AddVisitor> for MCaptcha {
|
||||||
#[rtype(result = "u32")]
|
#[rtype(result = "u32")]
|
||||||
pub struct GetCurrentVisitorCount;
|
pub struct GetCurrentVisitorCount;
|
||||||
|
|
||||||
impl Handler<GetCurrentVisitorCount> for MCaptcha {
|
impl Handler<GetCurrentVisitorCount> for Counter {
|
||||||
type Result = MessageResult<GetCurrentVisitorCount>;
|
type Result = MessageResult<GetCurrentVisitorCount>;
|
||||||
|
|
||||||
fn handle(&mut self, _: GetCurrentVisitorCount, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _: GetCurrentVisitorCount, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
@ -236,12 +201,12 @@ impl Handler<GetCurrentVisitorCount> for MCaptcha {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to stop [MCaptcha]
|
/// Message to stop [Counter]
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
pub struct Stop;
|
pub struct Stop;
|
||||||
|
|
||||||
impl Handler<Stop> for MCaptcha {
|
impl Handler<Stop> for Counter {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, _: Stop, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _: Stop, ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
@ -253,6 +218,8 @@ impl Handler<Stop> for MCaptcha {
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::defense::*;
|
use crate::defense::*;
|
||||||
|
use crate::errors::*;
|
||||||
|
use crate::master::MCaptchaBuilder;
|
||||||
|
|
||||||
// constants for testing
|
// constants for testing
|
||||||
// (visitor count, level)
|
// (visitor count, level)
|
||||||
|
@ -260,7 +227,7 @@ pub mod tests {
|
||||||
pub const LEVEL_2: (u32, u32) = (500, 500);
|
pub const LEVEL_2: (u32, u32) = (500, 500);
|
||||||
pub const DURATION: u64 = 5;
|
pub const DURATION: u64 = 5;
|
||||||
|
|
||||||
type MyActor = Addr<MCaptcha>;
|
type MyActor = Addr<Counter>;
|
||||||
|
|
||||||
pub fn get_defense() -> Defense {
|
pub fn get_defense() -> Defense {
|
||||||
DefenseBuilder::default()
|
DefenseBuilder::default()
|
||||||
|
@ -286,13 +253,17 @@ pub mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn race(addr: Addr<MCaptcha>, count: (u32, u32)) {
|
async fn race(addr: Addr<Counter>, count: (u32, u32)) {
|
||||||
for _ in 0..count.0 as usize - 1 {
|
for _ in 0..count.0 as usize - 1 {
|
||||||
let _ = addr.send(AddVisitor).await.unwrap();
|
let _ = addr.send(AddVisitor).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_counter() -> MCaptcha {
|
pub fn get_counter() -> Counter {
|
||||||
|
get_mcaptcha().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mcaptcha() -> MCaptcha {
|
||||||
MCaptchaBuilder::default()
|
MCaptchaBuilder::default()
|
||||||
.defense(get_defense())
|
.defense(get_defense())
|
||||||
.duration(DURATION)
|
.duration(DURATION)
|
|
@ -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 [MCaptcha] actors
|
//! [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;
|
||||||
|
@ -23,30 +23,28 @@ use std::time::Duration;
|
||||||
//use actix::clock::sleep;
|
//use actix::clock::sleep;
|
||||||
use actix::clock::delay_for;
|
use actix::clock::delay_for;
|
||||||
use actix::dev::*;
|
use actix::dev::*;
|
||||||
use derive_builder::Builder;
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use super::mcaptcha::MCaptcha;
|
use super::counter::Counter;
|
||||||
use crate::master::AddVisitor;
|
|
||||||
use crate::master::Master as MasterTrait;
|
use crate::master::Master as MasterTrait;
|
||||||
|
use crate::master::{AddSite, AddVisitor};
|
||||||
|
|
||||||
/// This Actor manages the [MCaptcha] actors.
|
/// This Actor manages the [Counter] actors.
|
||||||
/// A service can have several [MCaptcha] actors with
|
/// A service can have several [Counter] actors with
|
||||||
/// varying [Defense][crate::defense::Defense] configurations
|
/// varying [Defense][crate::defense::Defense] configurations
|
||||||
/// so a "master" actor is needed to manage them all
|
/// so a "master" actor is needed to manage them all
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Master {
|
pub struct Master {
|
||||||
sites: BTreeMap<String, (Option<()>, Addr<MCaptcha>)>,
|
sites: BTreeMap<String, (Option<()>, Addr<Counter>)>,
|
||||||
gc: u64,
|
gc: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MasterTrait for Master {}
|
impl MasterTrait for Master {}
|
||||||
|
|
||||||
impl Master {
|
impl Master {
|
||||||
/// add [MCaptcha] actor to [Master]
|
/// add [Counter] actor to [Master]
|
||||||
pub fn add_site(&mut self, details: AddSite) {
|
pub fn add_site(&mut self, addr: Addr<Counter>, id: String) {
|
||||||
self.sites
|
self.sites.insert(id, (None, addr.to_owned()));
|
||||||
.insert(details.id, (None, details.addr.to_owned()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// create new master
|
/// create new master
|
||||||
|
@ -58,8 +56,8 @@ impl Master {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get [MCaptcha] actor from [Master]
|
/// get [Counter] actor from [Master]
|
||||||
pub fn get_site<'a, 'b>(&'a mut self, id: &'b str) -> Option<Addr<MCaptcha>> {
|
pub fn get_site<'a, 'b>(&'a mut self, id: &'b str) -> Option<Addr<Counter>> {
|
||||||
let mut r = None;
|
let mut r = None;
|
||||||
if let Some((read_val, addr)) = self.sites.get_mut(id) {
|
if let Some((read_val, addr)) = self.sites.get_mut(id) {
|
||||||
r = Some(addr.clone());
|
r = Some(addr.clone());
|
||||||
|
@ -68,7 +66,7 @@ impl Master {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remvoes [MCaptcha] actor from [Master]
|
/// remvoes [Counter] actor from [Master]
|
||||||
pub fn rm_site(&mut self, id: &str) {
|
pub fn rm_site(&mut self, id: &str) {
|
||||||
self.sites.remove(id);
|
self.sites.remove(id);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +97,7 @@ impl Handler<AddVisitor> for Master {
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
let config = addr
|
let config = addr
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.send(super::mcaptcha::AddVisitor)
|
.send(super::counter::AddVisitor)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -112,9 +110,9 @@ impl Handler<AddVisitor> for Master {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to get an [MCaptcha] actor from master
|
/// Message to get an [Counter] actor from master
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "Option<Addr<MCaptcha>>")]
|
#[rtype(result = "Option<Addr<Counter>>")]
|
||||||
pub struct GetSite(pub String);
|
pub struct GetSite(pub String);
|
||||||
|
|
||||||
impl Handler<GetSite> for Master {
|
impl Handler<GetSite> for Master {
|
||||||
|
@ -130,7 +128,7 @@ impl Handler<GetSite> for Master {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to clean up master of [MCaptcha] actors with zero visitor count
|
/// Message to clean up master of [Counter] actors with zero visitor count
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
pub struct CleanUp;
|
pub struct CleanUp;
|
||||||
|
@ -145,7 +143,7 @@ impl Handler<CleanUp> for Master {
|
||||||
info!("init master actor cleanup up");
|
info!("init master actor cleanup up");
|
||||||
let task = async move {
|
let task = async move {
|
||||||
for (id, (new, addr)) in sites.iter() {
|
for (id, (new, addr)) in sites.iter() {
|
||||||
use super::mcaptcha::{GetCurrentVisitorCount, Stop};
|
use super::counter::{GetCurrentVisitorCount, Stop};
|
||||||
let visitor_count = addr.send(GetCurrentVisitorCount).await.unwrap();
|
let visitor_count = addr.send(GetCurrentVisitorCount).await.unwrap();
|
||||||
println!("{}", visitor_count);
|
println!("{}", visitor_count);
|
||||||
if visitor_count == 0 && new.is_some() {
|
if visitor_count == 0 && new.is_some() {
|
||||||
|
@ -165,7 +163,7 @@ impl Handler<CleanUp> for Master {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to delete [MCaptcha] actor
|
/// Message to delete [Counter] actor
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "()")]
|
#[rtype(result = "()")]
|
||||||
pub struct RemoveSite(pub String);
|
pub struct RemoveSite(pub String);
|
||||||
|
@ -178,43 +176,38 @@ impl Handler<RemoveSite> for Master {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to add an [MCaptcha] actor to [Master]
|
|
||||||
#[derive(Message, Builder)]
|
|
||||||
#[rtype(result = "()")]
|
|
||||||
pub struct AddSite {
|
|
||||||
pub id: String,
|
|
||||||
pub addr: Addr<MCaptcha>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler<AddSite> for Master {
|
impl Handler<AddSite> for Master {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, m: AddSite, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
self.add_site(m);
|
let counter: Counter = m.mcaptcha.into();
|
||||||
|
let addr = counter.start();
|
||||||
|
self.add_site(addr, m.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::master::embedded::mcaptcha::tests::*;
|
use crate::master::embedded::counter::tests::*;
|
||||||
|
use crate::master::AddSiteBuilder;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn master_actor_works() {
|
async fn master_actor_works() {
|
||||||
let addr = Master::new(1).start();
|
let addr = Master::new(1).start();
|
||||||
|
|
||||||
let id = "yo";
|
let id = "yo";
|
||||||
let mcaptcha = get_counter().start();
|
let mcaptcha = get_mcaptcha();
|
||||||
let msg = AddSiteBuilder::default()
|
let msg = AddSiteBuilder::default()
|
||||||
.id(id.into())
|
.id(id.into())
|
||||||
.addr(mcaptcha.clone())
|
.mcaptcha(mcaptcha.clone())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
addr.send(msg).await.unwrap();
|
addr.send(msg).await.unwrap();
|
||||||
|
|
||||||
let mcaptcha_addr = addr.send(GetSite(id.into())).await.unwrap();
|
let mcaptcha_addr = addr.send(GetSite(id.into())).await.unwrap();
|
||||||
assert_eq!(mcaptcha_addr, Some(mcaptcha));
|
assert!(mcaptcha_addr.is_some());
|
||||||
|
|
||||||
let addr_doesnt_exist = addr.send(GetSite("a".into())).await.unwrap();
|
let addr_doesnt_exist = addr.send(GetSite("a".into())).await.unwrap();
|
||||||
assert!(addr_doesnt_exist.is_none());
|
assert!(addr_doesnt_exist.is_none());
|
||||||
|
|
|
@ -16,5 +16,5 @@
|
||||||
* 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 counter;
|
||||||
pub mod master;
|
pub mod master;
|
||||||
pub mod mcaptcha;
|
|
||||||
|
|
|
@ -19,12 +19,18 @@
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
|
|
||||||
use actix::dev::*;
|
use actix::dev::*;
|
||||||
|
use derive_builder::Builder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod embedded;
|
pub mod embedded;
|
||||||
|
|
||||||
|
use crate::defense::Defense;
|
||||||
|
use crate::errors::*;
|
||||||
|
|
||||||
/// Describes actor handler trait impls that are required by a cache implementation
|
/// Describes actor handler trait impls that are required by a cache implementation
|
||||||
pub trait Master: actix::Actor + actix::Handler<AddVisitor> {}
|
pub trait Master: actix::Actor + actix::Handler<AddVisitor> + actix::Handler<AddSite> {}
|
||||||
|
|
||||||
|
//+ actix::Handler<AddSite>
|
||||||
|
|
||||||
/// Message to add visitor to an [MCaptcha] actor
|
/// Message to add visitor to an [MCaptcha] actor
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
|
@ -39,3 +45,68 @@ pub struct AddVisitorResult {
|
||||||
pub duration: u64,
|
pub duration: u64,
|
||||||
pub difficulty_factor: u32,
|
pub difficulty_factor: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Message to add an [Counter] actor to [Master]
|
||||||
|
#[derive(Message, Builder)]
|
||||||
|
#[rtype(result = "()")]
|
||||||
|
pub struct AddSite {
|
||||||
|
pub id: String,
|
||||||
|
pub mcaptcha: MCaptcha,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for [MCaptcha]
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MCaptchaBuilder {
|
||||||
|
visitor_threshold: u32,
|
||||||
|
defense: Option<Defense>,
|
||||||
|
duration: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MCaptchaBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
MCaptchaBuilder {
|
||||||
|
visitor_threshold: 0,
|
||||||
|
defense: None,
|
||||||
|
duration: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MCaptchaBuilder {
|
||||||
|
/// set defense
|
||||||
|
pub fn defense(&mut self, d: Defense) -> &mut Self {
|
||||||
|
self.defense = Some(d);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set duration
|
||||||
|
pub fn duration(&mut self, d: u64) -> &mut Self {
|
||||||
|
self.duration = Some(d);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds new [MCaptcha]
|
||||||
|
pub fn build(self: &mut MCaptchaBuilder) -> CaptchaResult<MCaptcha> {
|
||||||
|
if self.duration.is_none() {
|
||||||
|
Err(CaptchaError::PleaseSetValue("duration".into()))
|
||||||
|
} else if self.defense.is_none() {
|
||||||
|
Err(CaptchaError::PleaseSetValue("defense".into()))
|
||||||
|
} else if self.duration <= Some(0) {
|
||||||
|
Err(CaptchaError::CaptchaDurationZero)
|
||||||
|
} else {
|
||||||
|
let m = MCaptcha {
|
||||||
|
duration: self.duration.unwrap(),
|
||||||
|
defense: self.defense.clone().unwrap(),
|
||||||
|
visitor_threshold: self.visitor_threshold,
|
||||||
|
};
|
||||||
|
Ok(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
|
pub struct MCaptcha {
|
||||||
|
visitor_threshold: u32,
|
||||||
|
defense: Defense,
|
||||||
|
duration: u64,
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,8 @@ where
|
||||||
+ ToEnvelope<T, CacheResult>
|
+ ToEnvelope<T, CacheResult>
|
||||||
+ ToEnvelope<T, VerifyCaptchaResult>,
|
+ ToEnvelope<T, VerifyCaptchaResult>,
|
||||||
X: Master,
|
X: Master,
|
||||||
<X as actix::Actor>::Context: ToEnvelope<X, crate::master::AddVisitor>,
|
<X as actix::Actor>::Context:
|
||||||
|
ToEnvelope<X, crate::master::AddVisitor> + ToEnvelope<X, crate::master::AddSite>,
|
||||||
{
|
{
|
||||||
/// utility function to get difficulty factor of site `id` and cache it
|
/// utility function to get difficulty factor of site `id` and cache it
|
||||||
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
|
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
|
||||||
|
@ -123,21 +124,21 @@ mod tests {
|
||||||
use super::System;
|
use super::System;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::cache::HashCache;
|
use crate::cache::HashCache;
|
||||||
|
use crate::master::embedded::counter::tests::*;
|
||||||
use crate::master::embedded::master::Master;
|
use crate::master::embedded::master::Master;
|
||||||
use crate::master::embedded::master::*;
|
use crate::master::*;
|
||||||
use crate::master::embedded::mcaptcha::tests::*;
|
|
||||||
|
|
||||||
const MCAPTCHA_NAME: &str = "batsense.net";
|
const MCAPTCHA_NAME: &str = "batsense.net";
|
||||||
|
|
||||||
async fn boostrap_system(gc: u64) -> System<HashCache, Master> {
|
async fn boostrap_system(gc: u64) -> System<HashCache, Master> {
|
||||||
let master = Master::new(gc).start();
|
let master = Master::new(gc).start();
|
||||||
let mcaptcha = get_counter().start();
|
let mcaptcha = get_mcaptcha();
|
||||||
let pow = get_config();
|
let pow = get_config();
|
||||||
|
|
||||||
let cache = HashCache::default().start();
|
let cache = HashCache::default().start();
|
||||||
let msg = AddSiteBuilder::default()
|
let msg = AddSiteBuilder::default()
|
||||||
.id(MCAPTCHA_NAME.into())
|
.id(MCAPTCHA_NAME.into())
|
||||||
.addr(mcaptcha.clone())
|
.mcaptcha(mcaptcha)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue