token validation
This commit is contained in:
parent
6f83b3cd0c
commit
2d120d6791
6 changed files with 95 additions and 31 deletions
|
@ -6,6 +6,7 @@
|
||||||
as a result:
|
as a result:
|
||||||
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
|
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
|
||||||
- random token generation post `PoW` verification
|
- random token generation post `PoW` verification
|
||||||
|
- token validation
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
- `Cache` became `CachePoW` (`HashCache` extension)
|
- `Cache` became `CachePoW` (`HashCache` extension)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use m_captcha::{
|
use m_captcha::{
|
||||||
cache::HashCache,
|
cache::{messages::VerifyCaptchaResult, HashCache},
|
||||||
master::{AddSiteBuilder, Master},
|
master::{AddSiteBuilder, Master},
|
||||||
pow::{ConfigBuilder, Work},
|
pow::{ConfigBuilder, Work},
|
||||||
system::SystemBuilder,
|
system::SystemBuilder,
|
||||||
|
@ -107,11 +107,29 @@ async fn main() -> std::io::Result<()> {
|
||||||
key: mcaptcha_name.into(),
|
key: mcaptcha_name.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Server evaluates client's work. Returns true if everything
|
// mCAptcha evaluates client's work. Returns a token if everything
|
||||||
// checksout and Err() if something fishy is happening
|
// checksout and Err() if something fishy is happening
|
||||||
let res = system.verify_pow(payload.clone()).await;
|
let res = system.verify_pow(payload.clone()).await;
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
// TODO add server-sideverification
|
|
||||||
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
16
src/cache/hashcache.rs
vendored
16
src/cache/hashcache.rs
vendored
|
@ -61,13 +61,13 @@ impl HashCache {
|
||||||
|
|
||||||
// save captcha result
|
// save captcha result
|
||||||
fn save_captcha_result(&mut self, res: CacheResult) -> CaptchaResult<()> {
|
fn save_captcha_result(&mut self, res: CacheResult) -> CaptchaResult<()> {
|
||||||
self.result_map.insert(res.result, res.key);
|
self.result_map.insert(res.token, res.key);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify captcha result
|
// verify captcha result
|
||||||
fn verify_captcha_result(&mut self, challenge: VerifyCaptchaResult) -> CaptchaResult<bool> {
|
fn verify_captcha_result(&mut self, challenge: VerifyCaptchaResult) -> CaptchaResult<bool> {
|
||||||
if let Some(captcha_id) = self.remove_cache_result(&challenge.result) {
|
if let Some(captcha_id) = self.remove_cache_result(&challenge.token) {
|
||||||
if captcha_id == challenge.key {
|
if captcha_id == challenge.key {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -141,7 +141,7 @@ impl Handler<CacheResult> for HashCache {
|
||||||
|
|
||||||
let addr = ctx.address();
|
let addr = ctx.address();
|
||||||
let del_msg = DeleteCaptchaResult {
|
let del_msg = DeleteCaptchaResult {
|
||||||
result: msg.result.clone(),
|
token: msg.token.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let duration: Duration = Duration::new(msg.duration.clone(), 0);
|
let duration: Duration = Duration::new(msg.duration.clone(), 0);
|
||||||
|
@ -161,7 +161,7 @@ impl Handler<CacheResult> for HashCache {
|
||||||
impl Handler<DeleteCaptchaResult> for HashCache {
|
impl Handler<DeleteCaptchaResult> for HashCache {
|
||||||
type Result = MessageResult<DeleteCaptchaResult>;
|
type Result = MessageResult<DeleteCaptchaResult>;
|
||||||
fn handle(&mut self, msg: DeleteCaptchaResult, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: DeleteCaptchaResult, _ctx: &mut Self::Context) -> Self::Result {
|
||||||
self.remove_cache_result(&msg.result);
|
self.remove_cache_result(&msg.token);
|
||||||
MessageResult(Ok(()))
|
MessageResult(Ok(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ mod tests {
|
||||||
|
|
||||||
let add_cache = CacheResult {
|
let add_cache = CacheResult {
|
||||||
key: KEY.into(),
|
key: KEY.into(),
|
||||||
result: RES.into(),
|
token: RES.into(),
|
||||||
duration: DURATION,
|
duration: DURATION,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -260,14 +260,14 @@ mod tests {
|
||||||
|
|
||||||
let verify_msg = VerifyCaptchaResult {
|
let verify_msg = VerifyCaptchaResult {
|
||||||
key: KEY.into(),
|
key: KEY.into(),
|
||||||
result: RES.into(),
|
token: RES.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(addr.send(verify_msg).await.unwrap().unwrap());
|
assert!(addr.send(verify_msg).await.unwrap().unwrap());
|
||||||
|
|
||||||
let verify_msg = VerifyCaptchaResult {
|
let verify_msg = VerifyCaptchaResult {
|
||||||
key: "cz".into(),
|
key: "cz".into(),
|
||||||
result: RES.into(),
|
token: RES.into(),
|
||||||
};
|
};
|
||||||
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
|
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ mod tests {
|
||||||
|
|
||||||
let verify_msg = VerifyCaptchaResult {
|
let verify_msg = VerifyCaptchaResult {
|
||||||
key: KEY.into(),
|
key: KEY.into(),
|
||||||
result: RES.into(),
|
token: RES.into(),
|
||||||
};
|
};
|
||||||
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
|
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
|
||||||
}
|
}
|
||||||
|
|
14
src/cache/mod.rs
vendored
14
src/cache/mod.rs
vendored
|
@ -42,7 +42,7 @@ pub mod messages {
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
|
|
||||||
/// Message to cache PoW difficulty factor and string
|
/// Message to cache PoW difficulty factor and string
|
||||||
#[derive(Message, Serialize, Deserialize, Builder)]
|
#[derive(Message, Serialize, Deserialize, Builder, Clone)]
|
||||||
#[rtype(result = "CaptchaResult<()>")]
|
#[rtype(result = "CaptchaResult<()>")]
|
||||||
pub struct CachePoW {
|
pub struct CachePoW {
|
||||||
pub string: String,
|
pub string: String,
|
||||||
|
@ -68,7 +68,7 @@ pub mod messages {
|
||||||
#[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
|
#[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
|
||||||
pub struct RetrivePoW(pub String);
|
pub struct RetrivePoW(pub String);
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Default)]
|
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct CachedPoWConfig {
|
pub struct CachedPoWConfig {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub difficulty_factor: u32,
|
pub difficulty_factor: u32,
|
||||||
|
@ -86,7 +86,7 @@ pub mod messages {
|
||||||
#[derive(Message, Serialize, Deserialize, Builder)]
|
#[derive(Message, Serialize, Deserialize, Builder)]
|
||||||
#[rtype(result = "CaptchaResult<()>")]
|
#[rtype(result = "CaptchaResult<()>")]
|
||||||
pub struct CacheResult {
|
pub struct CacheResult {
|
||||||
pub result: String,
|
pub token: String,
|
||||||
// key is Captcha identifier
|
// key is Captcha identifier
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub duration: u64,
|
pub duration: u64,
|
||||||
|
@ -99,7 +99,7 @@ pub mod messages {
|
||||||
CacheResultBuilder::default()
|
CacheResultBuilder::default()
|
||||||
.key(c.key)
|
.key(c.key)
|
||||||
.duration(c.duration)
|
.duration(c.duration)
|
||||||
.result(get_random(32))
|
.token(get_random(32))
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,10 @@ pub mod messages {
|
||||||
|
|
||||||
/// Message to verify captcha result against
|
/// Message to verify captcha result against
|
||||||
/// the stored captcha key
|
/// the stored captcha key
|
||||||
#[derive(Message)]
|
#[derive(Message, Clone, Deserialize, Serialize)]
|
||||||
#[rtype(result = "CaptchaResult<bool>")]
|
#[rtype(result = "CaptchaResult<bool>")]
|
||||||
pub struct VerifyCaptchaResult {
|
pub struct VerifyCaptchaResult {
|
||||||
pub result: String,
|
pub token: String,
|
||||||
pub key: String,
|
pub key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,6 @@ pub mod messages {
|
||||||
#[derive(Message)]
|
#[derive(Message)]
|
||||||
#[rtype(result = "CaptchaResult<()>")]
|
#[rtype(result = "CaptchaResult<()>")]
|
||||||
pub struct DeleteCaptchaResult {
|
pub struct DeleteCaptchaResult {
|
||||||
pub result: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -47,7 +47,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use m_captcha::{
|
//! use m_captcha::{
|
||||||
//! cache::HashCache,
|
//! cache::{messages::VerifyCaptchaResult, HashCache},
|
||||||
//! master::{AddSiteBuilder, Master},
|
//! master::{AddSiteBuilder, Master},
|
||||||
//! pow::{ConfigBuilder, Work},
|
//! pow::{ConfigBuilder, Work},
|
||||||
//! system::SystemBuilder,
|
//! system::SystemBuilder,
|
||||||
|
@ -160,7 +160,26 @@
|
||||||
//! let res = system.verify_pow(payload.clone()).await;
|
//! let res = system.verify_pow(payload.clone()).await;
|
||||||
//! assert!(res.is_ok());
|
//! assert!(res.is_ok());
|
||||||
//!
|
//!
|
||||||
//! 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(())
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
|
@ -20,7 +20,7 @@ use actix::dev::*;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use pow_sha256::Config;
|
use pow_sha256::Config;
|
||||||
|
|
||||||
use crate::cache::messages;
|
use crate::cache::messages::*;
|
||||||
use crate::cache::Save;
|
use crate::cache::Save;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::master::Master;
|
use crate::master::Master;
|
||||||
|
@ -37,14 +37,13 @@ pub struct System<T: Save> {
|
||||||
impl<T> System<T>
|
impl<T> System<T>
|
||||||
where
|
where
|
||||||
T: Save,
|
T: Save,
|
||||||
<T as actix::Actor>::Context: ToEnvelope<T, messages::CachePoW>
|
<T as actix::Actor>::Context: ToEnvelope<T, CachePoW>
|
||||||
+ ToEnvelope<T, messages::RetrivePoW>
|
+ ToEnvelope<T, RetrivePoW>
|
||||||
+ ToEnvelope<T, messages::CacheResult>
|
+ ToEnvelope<T, CacheResult>
|
||||||
+ ToEnvelope<T, messages::VerifyCaptchaResult>,
|
+ ToEnvelope<T, VerifyCaptchaResult>,
|
||||||
{
|
{
|
||||||
/// utility function to get difficulty factor of site `id` and cache it
|
/// utility function to get difficulty factor of site `id` and cache it
|
||||||
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
|
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
|
||||||
use crate::cache::messages::CachePoWBuilder;
|
|
||||||
use crate::master::GetSite;
|
use crate::master::GetSite;
|
||||||
use crate::mcaptcha::AddVisitor;
|
use crate::mcaptcha::AddVisitor;
|
||||||
|
|
||||||
|
@ -55,8 +54,6 @@ where
|
||||||
let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap();
|
let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap();
|
||||||
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor);
|
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor);
|
||||||
|
|
||||||
// let cache_msg = CachePoW::new(&pow_config, &mcaptcha);
|
|
||||||
|
|
||||||
let cache_msg = CachePoWBuilder::default()
|
let cache_msg = CachePoWBuilder::default()
|
||||||
.string(pow_config.string.clone())
|
.string(pow_config.string.clone())
|
||||||
.difficulty_factor(mcaptcha.difficulty_factor)
|
.difficulty_factor(mcaptcha.difficulty_factor)
|
||||||
|
@ -71,8 +68,6 @@ where
|
||||||
|
|
||||||
/// utility function to verify [Work]
|
/// utility function to verify [Work]
|
||||||
pub async fn verify_pow(&self, work: Work) -> CaptchaResult<String> {
|
pub async fn verify_pow(&self, work: Work) -> CaptchaResult<String> {
|
||||||
use crate::cache::messages::*;
|
|
||||||
|
|
||||||
let string = work.string.clone();
|
let string = work.string.clone();
|
||||||
let msg = RetrivePoW(string.clone());
|
let msg = RetrivePoW(string.clone());
|
||||||
|
|
||||||
|
@ -102,10 +97,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg: CacheResult = cached_config.into();
|
let msg: CacheResult = cached_config.into();
|
||||||
let res = msg.result.clone();
|
let res = msg.token.clone();
|
||||||
self.cache.send(msg).await.unwrap()?;
|
self.cache.send(msg).await.unwrap()?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// utility function to validate verification tokens
|
||||||
|
pub async fn validate_verification_tokens(
|
||||||
|
&self,
|
||||||
|
msg: VerifyCaptchaResult,
|
||||||
|
) -> CaptchaResult<bool> {
|
||||||
|
self.cache.send(msg).await.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -159,13 +162,18 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn verify_pow_works() {
|
async fn verify_pow_works() {
|
||||||
|
// start system
|
||||||
let actors = boostrap_system(10).await;
|
let actors = boostrap_system(10).await;
|
||||||
|
// get work
|
||||||
let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap();
|
let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap();
|
||||||
|
// get config
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
|
||||||
|
// generate proof
|
||||||
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();
|
||||||
|
// generate proof payload
|
||||||
let mut payload = Work {
|
let mut payload = Work {
|
||||||
string: work_req.string,
|
string: work_req.string,
|
||||||
result: work.result,
|
result: work.result,
|
||||||
|
@ -173,9 +181,27 @@ mod tests {
|
||||||
key: MCAPTCHA_NAME.into(),
|
key: MCAPTCHA_NAME.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// verifiy proof
|
||||||
let res = actors.verify_pow(payload.clone()).await;
|
let res = actors.verify_pow(payload.clone()).await;
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
// verify validation token
|
||||||
|
let mut verifi_msg = VerifyCaptchaResult {
|
||||||
|
token: res.unwrap(),
|
||||||
|
key: MCAPTCHA_NAME.into(),
|
||||||
|
};
|
||||||
|
assert!(actors
|
||||||
|
.validate_verification_tokens(verifi_msg.clone())
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
// verify wrong validation token
|
||||||
|
verifi_msg.token = MCAPTCHA_NAME.into();
|
||||||
|
assert!(!actors
|
||||||
|
.validate_verification_tokens(verifi_msg)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
payload.string = "wrongstring".into();
|
payload.string = "wrongstring".into();
|
||||||
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));
|
||||||
|
|
Loading…
Reference in a new issue