token validation

This commit is contained in:
Aravinth Manivannan 2021-04-10 13:14:48 +05:30
parent 6f83b3cd0c
commit 2d120d6791
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
6 changed files with 95 additions and 31 deletions

View file

@ -6,6 +6,7 @@
as a result:
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
- random token generation post `PoW` verification
- token validation
## Changed
- `Cache` became `CachePoW` (`HashCache` extension)

View file

@ -1,5 +1,5 @@
use m_captcha::{
cache::HashCache,
cache::{messages::VerifyCaptchaResult, HashCache},
master::{AddSiteBuilder, Master},
pow::{ConfigBuilder, Work},
system::SystemBuilder,
@ -107,11 +107,29 @@ async fn main() -> std::io::Result<()> {
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
let res = system.verify_pow(payload.clone()).await;
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(())
}

View file

@ -61,13 +61,13 @@ impl HashCache {
// save captcha result
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(())
}
// verify captcha result
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 {
return Ok(true);
} else {
@ -141,7 +141,7 @@ impl Handler<CacheResult> for HashCache {
let addr = ctx.address();
let del_msg = DeleteCaptchaResult {
result: msg.result.clone(),
token: msg.token.clone(),
};
let duration: Duration = Duration::new(msg.duration.clone(), 0);
@ -161,7 +161,7 @@ impl Handler<CacheResult> for HashCache {
impl Handler<DeleteCaptchaResult> for HashCache {
type Result = MessageResult<DeleteCaptchaResult>;
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(()))
}
}
@ -252,7 +252,7 @@ mod tests {
let add_cache = CacheResult {
key: KEY.into(),
result: RES.into(),
token: RES.into(),
duration: DURATION,
};
@ -260,14 +260,14 @@ mod tests {
let verify_msg = VerifyCaptchaResult {
key: KEY.into(),
result: RES.into(),
token: RES.into(),
};
assert!(addr.send(verify_msg).await.unwrap().unwrap());
let verify_msg = VerifyCaptchaResult {
key: "cz".into(),
result: RES.into(),
token: RES.into(),
};
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
@ -276,7 +276,7 @@ mod tests {
let verify_msg = VerifyCaptchaResult {
key: KEY.into(),
result: RES.into(),
token: RES.into(),
};
assert!(!addr.send(verify_msg).await.unwrap().unwrap());
}

14
src/cache/mod.rs vendored
View file

@ -42,7 +42,7 @@ pub mod messages {
use crate::errors::*;
/// Message to cache PoW difficulty factor and string
#[derive(Message, Serialize, Deserialize, Builder)]
#[derive(Message, Serialize, Deserialize, Builder, Clone)]
#[rtype(result = "CaptchaResult<()>")]
pub struct CachePoW {
pub string: String,
@ -68,7 +68,7 @@ pub mod messages {
#[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
pub struct RetrivePoW(pub String);
#[derive(Clone, PartialEq, Debug, Default)]
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
pub struct CachedPoWConfig {
pub key: String,
pub difficulty_factor: u32,
@ -86,7 +86,7 @@ pub mod messages {
#[derive(Message, Serialize, Deserialize, Builder)]
#[rtype(result = "CaptchaResult<()>")]
pub struct CacheResult {
pub result: String,
pub token: String,
// key is Captcha identifier
pub key: String,
pub duration: u64,
@ -99,7 +99,7 @@ pub mod messages {
CacheResultBuilder::default()
.key(c.key)
.duration(c.duration)
.result(get_random(32))
.token(get_random(32))
.build()
.unwrap()
}
@ -107,10 +107,10 @@ pub mod messages {
/// Message to verify captcha result against
/// the stored captcha key
#[derive(Message)]
#[derive(Message, Clone, Deserialize, Serialize)]
#[rtype(result = "CaptchaResult<bool>")]
pub struct VerifyCaptchaResult {
pub result: String,
pub token: String,
pub key: String,
}
@ -118,6 +118,6 @@ pub mod messages {
#[derive(Message)]
#[rtype(result = "CaptchaResult<()>")]
pub struct DeleteCaptchaResult {
pub result: String,
pub token: String,
}
}

View file

@ -47,7 +47,7 @@
//!
//! ```rust
//! use m_captcha::{
//! cache::HashCache,
//! cache::{messages::VerifyCaptchaResult, HashCache},
//! master::{AddSiteBuilder, Master},
//! pow::{ConfigBuilder, Work},
//! system::SystemBuilder,
@ -160,7 +160,26 @@
//! let res = system.verify_pow(payload.clone()).await;
//! 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)]

View file

@ -20,7 +20,7 @@ use actix::dev::*;
use derive_builder::Builder;
use pow_sha256::Config;
use crate::cache::messages;
use crate::cache::messages::*;
use crate::cache::Save;
use crate::errors::*;
use crate::master::Master;
@ -37,14 +37,13 @@ pub struct System<T: Save> {
impl<T> System<T>
where
T: Save,
<T as actix::Actor>::Context: ToEnvelope<T, messages::CachePoW>
+ ToEnvelope<T, messages::RetrivePoW>
+ ToEnvelope<T, messages::CacheResult>
+ ToEnvelope<T, messages::VerifyCaptchaResult>,
<T as actix::Actor>::Context: ToEnvelope<T, CachePoW>
+ ToEnvelope<T, RetrivePoW>
+ ToEnvelope<T, CacheResult>
+ ToEnvelope<T, VerifyCaptchaResult>,
{
/// utility function to get difficulty factor of site `id` and cache it
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
use crate::cache::messages::CachePoWBuilder;
use crate::master::GetSite;
use crate::mcaptcha::AddVisitor;
@ -55,8 +54,6 @@ where
let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap();
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor);
// let cache_msg = CachePoW::new(&pow_config, &mcaptcha);
let cache_msg = CachePoWBuilder::default()
.string(pow_config.string.clone())
.difficulty_factor(mcaptcha.difficulty_factor)
@ -71,8 +68,6 @@ where
/// utility function to verify [Work]
pub async fn verify_pow(&self, work: Work) -> CaptchaResult<String> {
use crate::cache::messages::*;
let string = work.string.clone();
let msg = RetrivePoW(string.clone());
@ -102,10 +97,18 @@ where
}
let msg: CacheResult = cached_config.into();
let res = msg.result.clone();
let res = msg.token.clone();
self.cache.send(msg).await.unwrap()?;
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)]
@ -159,13 +162,18 @@ mod tests {
#[actix_rt::test]
async fn verify_pow_works() {
// start system
let actors = boostrap_system(10).await;
// get work
let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap();
// get config
let config = get_config();
// generate proof
let work = config
.prove_work(&work_req.string, work_req.difficulty_factor)
.unwrap();
// generate proof payload
let mut payload = Work {
string: work_req.string,
result: work.result,
@ -173,9 +181,27 @@ mod tests {
key: MCAPTCHA_NAME.into(),
};
// verifiy proof
let res = actors.verify_pow(payload.clone()).await;
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();
let res = actors.verify_pow(payload.clone()).await;
assert_eq!(res, Err(CaptchaError::StringNotFound));