tmp/examples/simple.rs

135 lines
4.7 KiB
Rust

use libmcaptcha::{
cache::{messages::VerifyCaptchaResult, HashCache},
master::embedded::master::Master,
master::AddSiteBuilder,
pow::{ConfigBuilder, Work},
system::SystemBuilder,
DefenseBuilder, LevelBuilder, MCaptchaBuilder,
};
// traits from actix needs to be in scope for starting actor
use actix::prelude::*;
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
// start cahce actor
// cache is used to store PoW requirements that are sent to clients
// This way, it can be verified that the client computed work over a config
// that _we_ sent. Offers protection against rainbow tables powered dictionary attacks
let cache = HashCache::default().start();
// create PoW config with unique salt. Salt has to be safely guarded.
// salts protect us from replay attacks
let pow = ConfigBuilder::default()
.salt("myrandomsaltisnotlongenoug".into())
.build()
.unwrap();
// start master actor. Master actor is responsible for managing MCaptcha actors
// each mCaptcha system should have only one master
let master = Master::new(60).start();
// Create system. System encapsulates master and cache and provides useful abstraction
// each mCaptcha system should have only one system
let system = SystemBuilder::default()
.master(master)
.cache(cache)
.pow(pow.clone())
.build()
.unwrap();
// configure defense. This is a per site configuration. A site can have several levels
// of defenses configured
let defense = DefenseBuilder::default()
// add as many defense as you see fit
.add_level(
LevelBuilder::default()
// visitor_threshold is the threshold/limit at which
// mCaptcha will adjust difficulty defense
// it is advisable to set small values for the first
// defense visitor_threshold and difficulty_factor
// as this will be the work that clients will be
// computing when there's no load
.visitor_threshold(50)
.difficulty_factor(500)
.unwrap()
.build()
.unwrap(),
)
.unwrap()
.add_level(
LevelBuilder::default()
.visitor_threshold(5000)
.difficulty_factor(50000)
.unwrap()
.build()
.unwrap(),
)
.unwrap()
.build()
.unwrap();
// create and start MCaptcha actor that uses the above defense configuration
// This is what manages the difficulty factor of sites that an mCaptcha protects
let mcaptcha = MCaptchaBuilder::default()
.defense(defense)
// leaky bucket algorithm's emission interval
.duration(30)
// .cache(cache)
.build()
.unwrap();
// unique value identifying an MCaptcha actor
let mcaptcha_name = "batsense.net";
// add MCaptcha to Master
let msg = AddSiteBuilder::default()
.id(mcaptcha_name.into())
.mcaptcha(mcaptcha)
.build()
.unwrap();
system.master.send(msg).await.unwrap();
// Get PoW config. Should be called everytime there's a visitor for a
// managed site(here mcaptcha_name)
let work_req = system.get_pow(mcaptcha_name.into()).await.unwrap();
// the following computation should be done on the client but for the purpose
// of this illustration, we are going to do it on the server it self
let work = pow
.prove_work(&work_req.string, work_req.difficulty_factor)
.unwrap();
// the payload that the client sends to the server
let payload = Work {
string: work_req.string,
result: work.result,
nonce: work.nonce,
key: mcaptcha_name.into(),
};
// mCAptcha evaluates client's work. Returns a token if everything
// checksout and Err() if something fishy is happening
let res = system.verify_pow(payload.clone()).await;
assert!(res.is_ok());
// The client should submit the token to the mCaptcha protected service
// The service should validate the token received from the client
// with the mCaptcha server before processing client's
// request
// mcaptcha protected service sends the following paylaod to mCaptcha
// server:
let verify_msg = VerifyCaptchaResult {
token: res.unwrap(),
key: mcaptcha_name.into(),
};
// on mCaptcha server:
let res = system.validate_verification_tokens(verify_msg).await;
// mCaptcha will return true if token is valid and false if
// token is invalid
assert!(res.is_ok());
assert!(res.unwrap());
Ok(())
}