feat: New SimpleGenerator to generate the captcha

Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
Awiteb 2024-08-12 20:11:42 +00:00
parent 28425d939f
commit c6956ad729
Signed by: awiteb
GPG key ID: 3F6B55640AA6682F
3 changed files with 134 additions and 40 deletions

View file

@ -1,40 +0,0 @@
// Copyright (c) 2024, Awiteb <a@4rs.nl>
// A captcha middleware for Salvo framework.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use crate::{CaptchaDifficulty, CaptchaName, CaptchaStorage};
/// Captcha generator, used to generate a new captcha image. This trait are implemented for all [`CaptchaStorage`].
pub trait CaptchaGenerator: CaptchaStorage {
/// Create a new captcha image and return the token and the image encoded as png. Will return None if the captcha crate failed to create the captcha.
///
/// The returned captcha image is 220x110 pixels.
///
/// For more information about the captcha name and difficulty, see the [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty).
fn new_captcha(
&self,
name: CaptchaName,
difficulty: CaptchaDifficulty,
) -> impl std::future::Future<Output = Result<Option<(String, Vec<u8>)>, Self::Error>> + Send
{
async {
let Some((captcha_answer, captcha_image)) =
captcha::by_name(difficulty, name).as_tuple()
else {
return Ok(None);
};
let token = self.store_answer(captcha_answer).await?;
Ok(Some((token, captcha_image)))
}
}
}
impl<T> CaptchaGenerator for T where T: CaptchaStorage {}

27
src/captcha_gen/mod.rs Normal file
View file

@ -0,0 +1,27 @@
// Copyright (c) 2024, Awiteb <a@4rs.nl>
// A captcha middleware for Salvo framework.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#[cfg(feature = "simple_generator")]
mod simple_generator;
#[cfg(feature = "simple_generator")]
pub use simple_generator::*;
/// Captcha generator, used to generate a new captcha image and answer.
pub trait CaptchaGenerator: Send {
/// The error type of the captcha generator
type Error: std::error::Error;
/// Create a new captcha image and return the answer and the image encoded as png
fn new_captcha(
&self,
) -> impl std::future::Future<Output = Result<(String, Vec<u8>), Self::Error>> + Send;
}

View file

@ -0,0 +1,107 @@
// Copyright (c) 2024, Awiteb <a@4rs.nl>
// A captcha middleware for Salvo framework.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
use crate::CaptchaGenerator;
use std::fmt::Display;
/// Supported captcha names
///
/// See [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty) for more information.
#[derive(Debug, Clone, Copy)]
pub enum CaptchaName {
/// Plain text, without any distortion
Normal,
/// Slightly twisted text
SlightlyTwisted,
/// Very twisted text
VeryTwisted,
}
/// Supported captcha difficulties
///
/// See [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty) for more information.
#[derive(Debug, Clone, Copy)]
pub enum CaptchaDifficulty {
/// Easy to read text
Easy,
/// Medium difficulty text
Medium,
/// Hard to read text
Hard,
}
impl From<CaptchaName> for captcha::CaptchaName {
/// Function to convert the [`CaptchaName`] to the [`captcha::CaptchaName`]
fn from(value: CaptchaName) -> Self {
match value {
CaptchaName::Normal => Self::Lucy,
CaptchaName::SlightlyTwisted => Self::Amelia,
CaptchaName::VeryTwisted => Self::Mila,
}
}
}
impl From<CaptchaDifficulty> for captcha::Difficulty {
/// Function to convert the [`CaptchaDifficulty`] to the [`captcha::Difficulty`]
fn from(value: CaptchaDifficulty) -> captcha::Difficulty {
match value {
CaptchaDifficulty::Easy => Self::Easy,
CaptchaDifficulty::Medium => Self::Medium,
CaptchaDifficulty::Hard => Self::Hard,
}
}
}
#[derive(Debug)]
/// Error type for the [`SimpleGenerator`]
pub enum SimpleGeneratorError {
/// Failed to encode the captcha to png image
FaildEncodedToPng,
}
impl Display for SimpleGeneratorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Faild to encode the captcha to png image")
}
}
impl std::error::Error for SimpleGeneratorError {}
/// The simple captcha generator
pub struct SimpleGenerator {
name: CaptchaName,
difficulty: CaptchaDifficulty,
}
impl SimpleGenerator {
/// Create new [`SimpleGenerator`] instance
pub const fn new(name: CaptchaName, difficulty: CaptchaDifficulty) -> Self {
Self { name, difficulty }
}
}
impl CaptchaGenerator for SimpleGenerator {
type Error = SimpleGeneratorError;
/// The returned captcha image is 220x110 pixels in png format.
///
/// For more information about the captcha name and difficulty, see the [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty).
async fn new_captcha(&self) -> Result<(String, Vec<u8>), Self::Error> {
let Some((captcha_answer, captcha_image)) =
captcha::by_name(self.difficulty.into(), self.name.into()).as_tuple()
else {
return Err(SimpleGeneratorError::FaildEncodedToPng);
};
Ok((captcha_answer, captcha_image))
}
}