feat: Add case insensitive option to the captcha

Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
Awiteb 2024-08-12 20:28:38 +00:00
parent b67ebe9aea
commit 29b4b80071
Signed by: awiteb
GPG key ID: 3F6B55640AA6682F
2 changed files with 19 additions and 1 deletions

View file

@ -73,6 +73,7 @@ async fn main() {
CaptchaBuilder::new(Arc::clone(&captcha_storage), CaptchaFormFinder::new()) CaptchaBuilder::new(Arc::clone(&captcha_storage), CaptchaFormFinder::new())
// Skip the captcha if the request path is /skipped // Skip the captcha if the request path is /skipped
.skipper(|req: &mut Request, _: &Depot| req.uri().path() == "/skipped") .skipper(|req: &mut Request, _: &Depot| req.uri().path() == "/skipped")
.case_insensitive()
.build(); .build();
let router = Router::new() let router = Router::new()

View file

@ -42,6 +42,8 @@ where
storage: Arc<S>, storage: Arc<S>,
/// The skipper of the captcha, used to skip the captcha check. /// The skipper of the captcha, used to skip the captcha check.
skipper: Box<dyn Skipper>, skipper: Box<dyn Skipper>,
/// The case sensitive of the captcha answer.
case_sensitive: bool,
} }
/// The captcha states of the request /// The captcha states of the request
@ -75,6 +77,7 @@ where
captcha_expired_after: Duration, captcha_expired_after: Duration,
clean_interval: Duration, clean_interval: Duration,
skipper: Box<dyn Skipper>, skipper: Box<dyn Skipper>,
case_sensitive: bool,
} }
impl<S, F> CaptchaBuilder<Arc<S>, F> impl<S, F> CaptchaBuilder<Arc<S>, F>
@ -90,9 +93,18 @@ where
captcha_expired_after: Duration::from_secs(60 * 5), captcha_expired_after: Duration::from_secs(60 * 5),
clean_interval: Duration::from_secs(60), clean_interval: Duration::from_secs(60),
skipper: Box::new(none_skipper), skipper: Box::new(none_skipper),
case_sensitive: true,
} }
} }
/// Remove the case sensitive of the captcha, default is case sensitive.
///
/// This will make the captcha case insensitive, for example, the answer "Hello" will be the same as "hello".
pub fn case_insensitive(mut self) -> Self {
self.case_sensitive = false;
self
}
/// Set the duration after which the captcha will be expired, default is 5 minutes. /// Set the duration after which the captcha will be expired, default is 5 minutes.
/// ///
/// After the captcha is expired, it will be removed from the storage, and the user needs to get a new captcha. /// After the captcha is expired, it will be removed from the storage, and the user needs to get a new captcha.
@ -125,6 +137,7 @@ where
self.captcha_expired_after, self.captcha_expired_after,
self.clean_interval, self.clean_interval,
self.skipper, self.skipper,
self.case_sensitive,
) )
} }
} }
@ -141,6 +154,7 @@ where
captcha_expired_after: Duration, captcha_expired_after: Duration,
clean_interval: Duration, clean_interval: Duration,
skipper: Box<dyn Skipper>, skipper: Box<dyn Skipper>,
case_sensitive: bool,
) -> Self { ) -> Self {
let task_storage = Arc::clone(&storage); let task_storage = Arc::clone(&storage);
@ -157,6 +171,7 @@ where
finder, finder,
storage, storage,
skipper, skipper,
case_sensitive,
} }
} }
} }
@ -224,7 +239,9 @@ where
match self.storage.get_answer(&token).await { match self.storage.get_answer(&token).await {
Ok(Some(captch_answer)) => { Ok(Some(captch_answer)) => {
log::info!("Captcha answer is exist in storage for token: {token}"); log::info!("Captcha answer is exist in storage for token: {token}");
if captch_answer == answer { if (captch_answer == answer && self.case_sensitive)
|| captch_answer.eq_ignore_ascii_case(&answer)
{
log::info!("Captcha answer is correct for token: {token}"); log::info!("Captcha answer is correct for token: {token}");
self.storage.clear_by_token(&token).await.ok(); self.storage.clear_by_token(&token).await.ok();
depot.insert(CAPTCHA_STATE_KEY, CaptchaState::Passed); depot.insert(CAPTCHA_STATE_KEY, CaptchaState::Passed);