cache expiry and singleretrival of cache vals

This commit is contained in:
Aravinth Manivannan 2021-03-09 12:17:19 +05:30
parent 80602a0ac9
commit 049bdd9eaa
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
4 changed files with 142 additions and 52 deletions

View file

@ -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
View file

@ -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);
} }

View file

@ -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));
} }
} }

View file

@ -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);
} }
} }