From c53fe2e3ffd81e7aabf1d960763a073f2f05b3cd Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 13 Jun 2023 19:23:23 +0530 Subject: [PATCH] feat: define internfaces to create,fetch and rm auth challenges --- db/db-core/Cargo.toml | 1 + db/db-core/src/lib.rs | 70 +++++++++++++++++++++++++++++++++++++++++ db/db-core/src/tests.rs | 11 +++++++ 3 files changed, 82 insertions(+) diff --git a/db/db-core/Cargo.toml b/db/db-core/Cargo.toml index 87225139..751168c0 100644 --- a/db/db-core/Cargo.toml +++ b/db/db-core/Cargo.toml @@ -15,6 +15,7 @@ serde = { version = "1", features = ["derive"]} url = { version = "2.2.2", features = ["serde"] } #libmcaptcha = { version = "0.2.2", git = "https://github.com/mCaptcha/libmcaptcha", features = ["minimal"], default-features = false, tag = "0.2.2"} libmcaptcha = { branch = "master", git = "https://github.com/mCaptcha/libmcaptcha", features = ["full"] } +uuid = { version = "1.3.3", features = ["v4", "serde"] } [features] default = [] diff --git a/db/db-core/src/lib.rs b/db/db-core/src/lib.rs index 8c33d95f..70a292a9 100644 --- a/db/db-core/src/lib.rs +++ b/db/db-core/src/lib.rs @@ -31,7 +31,10 @@ //! - [errors](crate::auth): error data structures used in this crate //! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting //! connection from pool +use std::str::FromStr; + use serde::{Deserialize, Serialize}; +use uuid::Uuid; pub use libmcaptcha::defense::Level; @@ -97,6 +100,64 @@ pub struct NameHash { pub hash: String, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +/// Email challenge reason +pub enum ChallengeReason { + /// challenge created to verify a newly registered user + EmailVerification, + /// Challenge created to verify a password reset request + PasswordReset, +} + +impl ChallengeReason { + pub fn to_str(&self) -> &'static str { + match self { + Self::EmailVerification => "email_verification", + Self::PasswordReset => "password_resset", + } + } +} + +impl ToString for ChallengeReason { + fn to_string(&self) -> String { + self.to_str().into() + } +} + +impl FromStr for ChallengeReason { + type Err = (); + fn from_str(s: &str) -> Result { + for reason in [Self::PasswordReset, Self::EmailVerification].iter() { + if s == reason.to_str() { + return Ok(reason.clone()); + } + } + Err(()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +/// Email challenge +pub struct Challenge { + /// challenge unique identifier + pub challenge: Uuid, + /// reason why the challenge was create + pub reason: ChallengeReason, +} + +impl Challenge { + /// create new Challenge instance for a given reason. Challenge text is auto-generated + pub fn new(reason: ChallengeReason) -> Self { + let challenge = Uuid::new_v4(); + Self { challenge, reason } + } + + /// Generate new ID (useful when ID clashes) + pub fn new_id(&mut self) { + self.challenge = Uuid::new_v4(); + } +} + #[async_trait] /// mCaptcha's database requirements. To implement support for $Database, kindly implement this /// trait. @@ -250,6 +311,15 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase { /// fetch PoWConfig confirms async fn fetch_confirm(&self, user: &str, key: &str) -> DBResult>; + + /// Record challenge in database + async fn new_challenge(&self, challenge: &mut Challenge) -> DBResult<()>; + + /// Record challenge in database + async fn fetch_challenge(&self, challenge: &Challenge) -> DBResult; + + /// Delete a challenge from database + async fn delete_challenge(&self, challenge: &Challenge) -> DBResult<()>; } #[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)] diff --git a/db/db-core/src/tests.rs b/db/db-core/src/tests.rs index 2ca6786b..543d600f 100644 --- a/db/db-core/src/tests.rs +++ b/db/db-core/src/tests.rs @@ -296,3 +296,14 @@ pub async fn database_works<'a, T: MCDatabase>( db.delete_captcha(p.username, p.username).await.unwrap(); assert!(!db.captcha_exists(Some(p.username), c.key).await.unwrap()); } + +/// test all challenge routines +pub async fn challenges_works<'a, T: MCDatabase>(db: &T) { + let mut challenge = Challenge::new(ChallengeReason::PasswordReset); + db.new_challenge(&mut challenge).await.unwrap(); + db.new_challenge(&mut challenge).await.unwrap(); + let c = db.fetch_challenge(&challenge).await.unwrap(); + assert_eq!(c, challenge); + db.delete_challenge(&challenge).await.unwrap(); + assert!(db.fetch_challenge(&challenge).await.is_err()) +}