cache expiry and singleretrival of cache vals
This commit is contained in:
parent
80602a0ac9
commit
049bdd9eaa
4 changed files with 142 additions and 52 deletions
81
src/cache/hashcache.rs
vendored
81
src/cache/hashcache.rs
vendored
|
@ -23,34 +23,32 @@ use actix::prelude::*;
|
||||||
use super::messages::*;
|
use super::messages::*;
|
||||||
use super::Save;
|
use super::Save;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::pow::PoWConfig;
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
/// cache datastructure implementing [Save]
|
/// cache datastructure implementing [Save]
|
||||||
pub struct HashCache(HashMap<String, u32>);
|
pub struct HashCache(HashMap<String, u32>);
|
||||||
|
|
||||||
impl HashCache {
|
impl HashCache {
|
||||||
fn save(&mut self, config: PoWConfig) -> CaptchaResult<()> {
|
// save [PoWConfig] to cache
|
||||||
|
fn save(&mut self, config: Cache) -> CaptchaResult<()> {
|
||||||
self.0.insert(config.string, config.difficulty_factor);
|
self.0.insert(config.string, config.difficulty_factor);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// retrive [PoWConfig] from cache. Deletes config post retrival
|
||||||
fn retrive(&mut self, string: String) -> CaptchaResult<Option<u32>> {
|
fn retrive(&mut self, string: String) -> CaptchaResult<Option<u32>> {
|
||||||
if let Some(difficulty_factor) = self.0.get(&string) {
|
if let Some(difficulty_factor) = self.remove(&string) {
|
||||||
Ok(Some(difficulty_factor.to_owned()))
|
Ok(Some(difficulty_factor.to_owned()))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO cache of pow configs need to have lifetimes to prevent replay attacks
|
// delete [PoWConfig] from cache
|
||||||
* where lifetime = site's cool down period so that people can't generate pow
|
fn remove(&mut self, string: &str) -> Option<u32> {
|
||||||
* configs when the site is cool and use them later with rainbow tables
|
self.0.remove(string)
|
||||||
* when it's under attack.
|
}
|
||||||
*
|
}
|
||||||
* This comment stays until this feature is implemented.
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl Save for HashCache {}
|
impl Save for HashCache {}
|
||||||
|
|
||||||
|
@ -61,8 +59,31 @@ impl Actor for HashCache {
|
||||||
/// cache a PoWConfig
|
/// cache a PoWConfig
|
||||||
impl Handler<Cache> for HashCache {
|
impl Handler<Cache> for HashCache {
|
||||||
type Result = MessageResult<Cache>;
|
type Result = MessageResult<Cache>;
|
||||||
fn handle(&mut self, msg: Cache, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: Cache, ctx: &mut Self::Context) -> Self::Result {
|
||||||
MessageResult(self.save(msg.0))
|
use actix::clock::delay_for;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
let addr = ctx.address();
|
||||||
|
let del_msg = DeleteString(msg.string.clone());
|
||||||
|
|
||||||
|
let duration: Duration = Duration::new(msg.duration.clone(), 0);
|
||||||
|
let wait_for = async move {
|
||||||
|
delay_for(duration).await;
|
||||||
|
addr.send(del_msg).await.unwrap().unwrap();
|
||||||
|
}
|
||||||
|
.into_actor(self);
|
||||||
|
ctx.spawn(wait_for);
|
||||||
|
|
||||||
|
MessageResult(self.save(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delte a PoWConfig
|
||||||
|
impl Handler<DeleteString> for HashCache {
|
||||||
|
type Result = MessageResult<DeleteString>;
|
||||||
|
fn handle(&mut self, msg: DeleteString, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
|
self.remove(&msg.0);
|
||||||
|
MessageResult(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,14 +98,38 @@ impl Handler<Retrive> for HashCache {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::mcaptcha::VisitorResult;
|
||||||
|
use crate::pow::PoWConfig;
|
||||||
|
|
||||||
|
async fn sleep(time: u64) {
|
||||||
|
use actix::clock::delay_for;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
let duration: Duration = Duration::new(time, 0);
|
||||||
|
delay_for(duration).await;
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn hashcache_works() {
|
async fn hashcache_works() {
|
||||||
|
const DIFFICULTY_FACTOR: u32 = 54;
|
||||||
|
const DURATION: u64 = 5;
|
||||||
let addr = HashCache::default().start();
|
let addr = HashCache::default().start();
|
||||||
let cache: PoWConfig = PoWConfig::new(54);
|
let pow: PoWConfig = PoWConfig::new(DIFFICULTY_FACTOR);
|
||||||
let string = cache.string.clone();
|
let visitor_result = VisitorResult {
|
||||||
addr.send(Cache(cache)).await.unwrap().unwrap();
|
difficulty_factor: DIFFICULTY_FACTOR,
|
||||||
let difficulty_factor = addr.send(Retrive(string)).await.unwrap().unwrap();
|
duration: DURATION,
|
||||||
assert_eq!(difficulty_factor.unwrap(), 54);
|
};
|
||||||
|
let string = pow.string.clone();
|
||||||
|
let msg = Cache::new(&pow, &visitor_result);
|
||||||
|
|
||||||
|
addr.send(msg).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
let cache_difficulty_factor = addr.send(Retrive(string.clone())).await.unwrap().unwrap();
|
||||||
|
assert_eq!(DIFFICULTY_FACTOR, cache_difficulty_factor.unwrap());
|
||||||
|
|
||||||
|
sleep(DURATION + DURATION).await;
|
||||||
|
|
||||||
|
let expired_string = addr.send(Retrive(string)).await.unwrap().unwrap();
|
||||||
|
assert_eq!(None, expired_string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
40
src/cache/mod.rs
vendored
40
src/cache/mod.rs
vendored
|
@ -23,22 +23,48 @@ use messages::*;
|
||||||
pub mod hashcache;
|
pub mod hashcache;
|
||||||
|
|
||||||
/// 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 Save: actix::Actor + actix::Handler<Retrive> + actix::Handler<Cache> {}
|
pub trait Save:
|
||||||
|
actix::Actor + actix::Handler<Retrive> + actix::Handler<Cache> + actix::Handler<DeleteString>
|
||||||
|
{
|
||||||
|
}
|
||||||
pub mod messages {
|
pub mod messages {
|
||||||
//! Messages that can be sent to cache data structures implementing [Save][super::Save]
|
//! Messages that can be sent to cache data structures implementing [Save][super::Save]
|
||||||
use crate::pow::PoWConfig;
|
|
||||||
use actix::dev::*;
|
use actix::dev::*;
|
||||||
|
use derive_builder::Builder;
|
||||||
|
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
|
use crate::mcaptcha::VisitorResult;
|
||||||
|
use crate::pow::PoWConfig;
|
||||||
|
|
||||||
/// Message to decrement the visitor count
|
/// Message to cache PoW difficulty factor and string
|
||||||
#[derive(Message)]
|
#[derive(Message, Builder)]
|
||||||
#[rtype(result = "CaptchaResult<()>")]
|
#[rtype(result = "CaptchaResult<()>")]
|
||||||
pub struct Cache(pub PoWConfig);
|
pub struct Cache {
|
||||||
|
pub string: String,
|
||||||
|
pub difficulty_factor: u32,
|
||||||
|
pub duration: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Message to decrement the visitor count
|
impl Cache {
|
||||||
|
pub fn new(p: &PoWConfig, v: &VisitorResult) -> Self {
|
||||||
|
CacheBuilder::default()
|
||||||
|
.string(p.string.clone())
|
||||||
|
.difficulty_factor(v.difficulty_factor)
|
||||||
|
.duration(v.duration)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message to retrive the the difficulty factor for the specified
|
||||||
|
/// string from the cache
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "CaptchaResult<Option<u32>>")]
|
#[rtype(result = "CaptchaResult<Option<u32>>")]
|
||||||
pub struct Retrive(pub String);
|
pub struct Retrive(pub String);
|
||||||
|
|
||||||
|
/// Message to delete cached PoW difficulty factor and string
|
||||||
|
/// when they expire
|
||||||
|
#[derive(Message)]
|
||||||
|
#[rtype(result = "CaptchaResult<()>")]
|
||||||
|
pub struct DeleteString(pub String);
|
||||||
}
|
}
|
||||||
|
|
28
src/data.rs
28
src/data.rs
|
@ -49,13 +49,11 @@ where
|
||||||
if site_addr.is_none() {
|
if site_addr.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let difficulty_factor = site_addr.unwrap().send(Visitor).await.unwrap();
|
let mcaptcha = site_addr.unwrap().send(Visitor).await.unwrap();
|
||||||
let pow_config = PoWConfig::new(difficulty_factor);
|
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor);
|
||||||
self.cache
|
|
||||||
.send(Cache(pow_config.clone()))
|
let cache_msg = Cache::new(&pow_config, &mcaptcha);
|
||||||
.await
|
self.cache.send(cache_msg).await.unwrap().unwrap();
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
Some(pow_config)
|
Some(pow_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,14 +136,6 @@ mod tests {
|
||||||
let work = config
|
let work = config
|
||||||
.prove_work(&work_req.string, work_req.difficulty_factor)
|
.prove_work(&work_req.string, work_req.difficulty_factor)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let insufficient_work = config.prove_work(&work_req.string, 1).unwrap();
|
|
||||||
let insufficient_work_payload = Work {
|
|
||||||
string: work_req.string.clone(),
|
|
||||||
result: insufficient_work.result,
|
|
||||||
nonce: insufficient_work.nonce,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut payload = Work {
|
let mut payload = Work {
|
||||||
string: work_req.string,
|
string: work_req.string,
|
||||||
result: work.result,
|
result: work.result,
|
||||||
|
@ -159,8 +149,14 @@ mod tests {
|
||||||
let res = actors.verify_pow(payload.clone()).await;
|
let res = actors.verify_pow(payload.clone()).await;
|
||||||
assert_eq!(res, Err(CaptchaError::StringNotFound));
|
assert_eq!(res, Err(CaptchaError::StringNotFound));
|
||||||
|
|
||||||
|
let insufficient_work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap();
|
||||||
|
let insufficient_work = config.prove_work(&insufficient_work_req.string, 1).unwrap();
|
||||||
|
let insufficient_work_payload = Work {
|
||||||
|
string: insufficient_work_req.string,
|
||||||
|
result: insufficient_work.result,
|
||||||
|
nonce: insufficient_work.nonce,
|
||||||
|
};
|
||||||
let res = actors.verify_pow(insufficient_work_payload.clone()).await;
|
let res = actors.verify_pow(insufficient_work_payload.clone()).await;
|
||||||
|
|
||||||
assert_eq!(res, Err(CaptchaError::InsuffiencientDifficulty));
|
assert_eq!(res, Err(CaptchaError::InsuffiencientDifficulty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,11 @@ impl MCaptcha {
|
||||||
pub fn get_difficulty(&self) -> u32 {
|
pub fn get_difficulty(&self) -> u32 {
|
||||||
self.defense.get_difficulty()
|
self.defense.get_difficulty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get [MCaptcha]'s lifetime
|
||||||
|
pub fn get_duration(&self) -> u64 {
|
||||||
|
self.duration
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Actor for MCaptcha {
|
impl Actor for MCaptcha {
|
||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
|
@ -129,12 +134,30 @@ impl Handler<DeleteVisitor> for MCaptcha {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to increment the visitor count
|
/// Message to increment the visitor count
|
||||||
|
/// returns difficulty factor and lifetime
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "u32")]
|
#[rtype(result = "VisitorResult")]
|
||||||
pub struct Visitor;
|
pub struct Visitor;
|
||||||
|
|
||||||
|
/// Struct representing the return datatime of
|
||||||
|
/// [Visitor] message. Contains MCaptcha lifetime
|
||||||
|
/// and difficulty factor
|
||||||
|
pub struct VisitorResult {
|
||||||
|
pub duration: u64,
|
||||||
|
pub difficulty_factor: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VisitorResult {
|
||||||
|
fn new(m: &MCaptcha) -> Self {
|
||||||
|
VisitorResult {
|
||||||
|
duration: m.get_duration(),
|
||||||
|
difficulty_factor: m.get_difficulty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler<Visitor> for MCaptcha {
|
impl Handler<Visitor> for MCaptcha {
|
||||||
type Result = u32;
|
type Result = MessageResult<Visitor>;
|
||||||
|
|
||||||
fn handle(&mut self, _: Visitor, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, _: Visitor, ctx: &mut Self::Context) -> Self::Result {
|
||||||
use actix::clock::delay_for;
|
use actix::clock::delay_for;
|
||||||
|
@ -150,7 +173,7 @@ impl Handler<Visitor> for MCaptcha {
|
||||||
ctx.spawn(wait_for);
|
ctx.spawn(wait_for);
|
||||||
|
|
||||||
self.add_visitor();
|
self.add_visitor();
|
||||||
self.get_difficulty()
|
MessageResult(VisitorResult::new(&self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,12 +232,12 @@ pub mod tests {
|
||||||
async fn counter_defense_tightenup_works() {
|
async fn counter_defense_tightenup_works() {
|
||||||
let addr: MyActor = get_counter().start();
|
let addr: MyActor = get_counter().start();
|
||||||
|
|
||||||
let mut difficulty_factor = addr.send(Visitor).await.unwrap();
|
let mut mcaptcha = addr.send(Visitor).await.unwrap();
|
||||||
assert_eq!(difficulty_factor, LEVEL_1.0);
|
assert_eq!(mcaptcha.difficulty_factor, LEVEL_1.0);
|
||||||
|
|
||||||
race(addr.clone(), LEVEL_2).await;
|
race(addr.clone(), LEVEL_2).await;
|
||||||
difficulty_factor = addr.send(Visitor).await.unwrap();
|
mcaptcha = addr.send(Visitor).await.unwrap();
|
||||||
assert_eq!(difficulty_factor, LEVEL_2.1);
|
assert_eq!(mcaptcha.difficulty_factor, LEVEL_2.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -224,13 +247,13 @@ pub mod tests {
|
||||||
|
|
||||||
race(addr.clone(), LEVEL_2).await;
|
race(addr.clone(), LEVEL_2).await;
|
||||||
race(addr.clone(), LEVEL_2).await;
|
race(addr.clone(), LEVEL_2).await;
|
||||||
let mut difficulty_factor = addr.send(Visitor).await.unwrap();
|
let mut mcaptcha = addr.send(Visitor).await.unwrap();
|
||||||
assert_eq!(difficulty_factor, LEVEL_2.1);
|
assert_eq!(mcaptcha.difficulty_factor, LEVEL_2.1);
|
||||||
|
|
||||||
let duration = Duration::new(DURATION, 0);
|
let duration = Duration::new(DURATION, 0);
|
||||||
delay_for(duration).await;
|
delay_for(duration).await;
|
||||||
|
|
||||||
difficulty_factor = addr.send(Visitor).await.unwrap();
|
mcaptcha = addr.send(Visitor).await.unwrap();
|
||||||
assert_eq!(difficulty_factor, LEVEL_1.1);
|
assert_eq!(mcaptcha.difficulty_factor, LEVEL_1.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue