68 lines
1.9 KiB
Rust
68 lines
1.9 KiB
Rust
use crate::AppService;
|
|
use crate::auth::rsa::RsaResponse;
|
|
use crate::error::AppError;
|
|
use session::Session;
|
|
use utoipa::{IntoParams, ToSchema};
|
|
|
|
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, ToSchema, IntoParams)]
|
|
pub struct CaptchaQuery {
|
|
pub w: u32,
|
|
pub h: u32,
|
|
pub dark: bool,
|
|
pub rsa: bool,
|
|
}
|
|
|
|
#[derive(serde::Serialize, ToSchema)]
|
|
pub struct CaptchaResponse {
|
|
pub base64: String,
|
|
pub rsa: Option<RsaResponse>,
|
|
pub req: CaptchaQuery,
|
|
}
|
|
|
|
impl AppService {
|
|
const CAPTCHA_KEY: &'static str = "captcha";
|
|
const CAPTCHA_LENGTH: usize = 4;
|
|
pub async fn auth_captcha(
|
|
&self,
|
|
context: &Session,
|
|
query: CaptchaQuery,
|
|
) -> Result<CaptchaResponse, AppError> {
|
|
let CaptchaQuery { w, h, dark, rsa } = query;
|
|
let captcha = captcha_rs::CaptchaBuilder::new()
|
|
.width(w)
|
|
.height(h)
|
|
.dark_mode(dark)
|
|
.length(Self::CAPTCHA_LENGTH)
|
|
.build();
|
|
let base64 = captcha.to_base64();
|
|
let text = captcha.text;
|
|
context.insert(Self::CAPTCHA_KEY, text).ok();
|
|
Ok(CaptchaResponse {
|
|
base64,
|
|
rsa: if rsa {
|
|
Some(self.auth_rsa(context).await?)
|
|
} else {
|
|
None
|
|
},
|
|
req: CaptchaQuery { w, h, dark, rsa },
|
|
})
|
|
}
|
|
pub async fn auth_check_captcha(
|
|
&self,
|
|
context: &Session,
|
|
captcha: String,
|
|
) -> Result<(), AppError> {
|
|
let text = context
|
|
.get::<String>(Self::CAPTCHA_KEY)
|
|
.map_err(|_| AppError::CaptchaError)?
|
|
.ok_or(AppError::CaptchaError)?;
|
|
if text.to_lowercase() != captcha.to_lowercase() {
|
|
context.remove(Self::CAPTCHA_KEY);
|
|
slog::warn!(self.logs, "Captcha verification failed"; "ip" => context.ip_address());
|
|
return Err(AppError::CaptchaError);
|
|
}
|
|
context.remove(Self::CAPTCHA_KEY);
|
|
Ok(())
|
|
}
|
|
}
|