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:
|
||||
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
|
||||
- random token generation post `PoW` verification
|
||||
- token validation
|
||||
|
||||
## Changed
|
||||
- `Cache` became `CachePoW` (`HashCache` extension)
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
16
src/cache/hashcache.rs
vendored
16
src/cache/hashcache.rs
vendored
|
@ -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
14
src/cache/mod.rs
vendored
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -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)]
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue