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::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
40
src/cache/mod.rs
vendored
|
@ -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);
|
||||
}
|
||||
|
|
28
src/data.rs
28
src/data.rs
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue