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::Save;
use crate::errors::*;
use crate::pow::PoWConfig;
#[derive(Clone, Default)]
/// cache datastructure implementing [Save]
pub struct HashCache(HashMap<String, u32>);
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);
Ok(())
}
// retrive [PoWConfig] from cache. Deletes config post retrival
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()))
} else {
Ok(None)
}
}
}
/* TODO cache of pow configs need to have lifetimes to prevent replay attacks
* where lifetime = site's cool down period so that people can't generate pow
* configs when the site is cool and use them later with rainbow tables
* when it's under attack.
*
* This comment stays until this feature is implemented.
*/
// delete [PoWConfig] from cache
fn remove(&mut self, string: &str) -> Option<u32> {
self.0.remove(string)
}
}
impl Save for HashCache {}
@ -61,8 +59,31 @@ impl Actor for HashCache {
/// cache a PoWConfig
impl Handler<Cache> for HashCache {
type Result = MessageResult<Cache>;
fn handle(&mut self, msg: Cache, _ctx: &mut Self::Context) -> Self::Result {
MessageResult(self.save(msg.0))
fn handle(&mut self, msg: Cache, ctx: &mut Self::Context) -> Self::Result {
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)]
mod tests {
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]
async fn hashcache_works() {
const DIFFICULTY_FACTOR: u32 = 54;
const DURATION: u64 = 5;
let addr = HashCache::default().start();
let cache: PoWConfig = PoWConfig::new(54);
let string = cache.string.clone();
addr.send(Cache(cache)).await.unwrap().unwrap();
let difficulty_factor = addr.send(Retrive(string)).await.unwrap().unwrap();
assert_eq!(difficulty_factor.unwrap(), 54);
let pow: PoWConfig = PoWConfig::new(DIFFICULTY_FACTOR);
let visitor_result = VisitorResult {
difficulty_factor: DIFFICULTY_FACTOR,
duration: DURATION,
};
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;
/// 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 {
//! Messages that can be sent to cache data structures implementing [Save][super::Save]
use crate::pow::PoWConfig;
use actix::dev::*;
use derive_builder::Builder;
use crate::errors::*;
use crate::mcaptcha::VisitorResult;
use crate::pow::PoWConfig;
/// Message to decrement the visitor count
#[derive(Message)]
/// Message to cache PoW difficulty factor and string
#[derive(Message, Builder)]
#[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)]
#[rtype(result = "CaptchaResult<Option<u32>>")]
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() {
return None;
}
let difficulty_factor = site_addr.unwrap().send(Visitor).await.unwrap();
let pow_config = PoWConfig::new(difficulty_factor);
self.cache
.send(Cache(pow_config.clone()))
.await
.unwrap()
.unwrap();
let mcaptcha = site_addr.unwrap().send(Visitor).await.unwrap();
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor);
let cache_msg = Cache::new(&pow_config, &mcaptcha);
self.cache.send(cache_msg).await.unwrap().unwrap();
Some(pow_config)
}
@ -138,14 +136,6 @@ mod tests {
let work = config
.prove_work(&work_req.string, work_req.difficulty_factor)
.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 {
string: work_req.string,
result: work.result,
@ -159,8 +149,14 @@ mod tests {
let res = actors.verify_pow(payload.clone()).await;
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;
assert_eq!(res, Err(CaptchaError::InsuffiencientDifficulty));
}
}

View file

@ -111,6 +111,11 @@ impl MCaptcha {
pub fn get_difficulty(&self) -> u32 {
self.defense.get_difficulty()
}
/// get [MCaptcha]'s lifetime
pub fn get_duration(&self) -> u64 {
self.duration
}
}
impl Actor for MCaptcha {
type Context = Context<Self>;
@ -129,12 +134,30 @@ impl Handler<DeleteVisitor> for MCaptcha {
}
/// Message to increment the visitor count
/// returns difficulty factor and lifetime
#[derive(Message)]
#[rtype(result = "u32")]
#[rtype(result = "VisitorResult")]
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 {
type Result = u32;
type Result = MessageResult<Visitor>;
fn handle(&mut self, _: Visitor, ctx: &mut Self::Context) -> Self::Result {
use actix::clock::delay_for;
@ -150,7 +173,7 @@ impl Handler<Visitor> for MCaptcha {
ctx.spawn(wait_for);
self.add_visitor();
self.get_difficulty()
MessageResult(VisitorResult::new(&self))
}
}
@ -209,12 +232,12 @@ pub mod tests {
async fn counter_defense_tightenup_works() {
let addr: MyActor = get_counter().start();
let mut difficulty_factor = addr.send(Visitor).await.unwrap();
assert_eq!(difficulty_factor, LEVEL_1.0);
let mut mcaptcha = addr.send(Visitor).await.unwrap();
assert_eq!(mcaptcha.difficulty_factor, LEVEL_1.0);
race(addr.clone(), LEVEL_2).await;
difficulty_factor = addr.send(Visitor).await.unwrap();
assert_eq!(difficulty_factor, LEVEL_2.1);
mcaptcha = addr.send(Visitor).await.unwrap();
assert_eq!(mcaptcha.difficulty_factor, LEVEL_2.1);
}
#[actix_rt::test]
@ -224,13 +247,13 @@ pub mod tests {
race(addr.clone(), LEVEL_2).await;
race(addr.clone(), LEVEL_2).await;
let mut difficulty_factor = addr.send(Visitor).await.unwrap();
assert_eq!(difficulty_factor, LEVEL_2.1);
let mut mcaptcha = addr.send(Visitor).await.unwrap();
assert_eq!(mcaptcha.difficulty_factor, LEVEL_2.1);
let duration = Duration::new(DURATION, 0);
delay_for(duration).await;
difficulty_factor = addr.send(Visitor).await.unwrap();
assert_eq!(difficulty_factor, LEVEL_1.1);
mcaptcha = addr.send(Visitor).await.unwrap();
assert_eq!(mcaptcha.difficulty_factor, LEVEL_1.1);
}
}